intro

PowerBASIC

Console Compiler 4

KeyWord Quick Finder

Introducing PB/CC 4.0 for Windows

What is PB/CC?

New Statements and Functions

Writing Programs in PBCC

Command Summary

Data Handling

Programming Reference

Register

Technical Support

Downloads

Peer Support Forums

http://www.powerbasic.com

Telephone: +1 (941) 408-8700

Copyright © 1996 - 2007 PowerBASIC Inc.


Introducing PB/CC 4.0 for Windows

Introducing PB/CC 4 for Windows

Welcome to PB/CC, the PowerBASIC Console Compiler for Windows. Our favorite slogan is "We put the Power in BASIC", and we sincerely believe you will find this to be true. With compilation speeds of 1 million lines per minute, unrivaled performance, and the smallest executables in the industry, PowerBASIC has become the new standard of comparison in Windows programming. Thank you for joining us in the War on Bloatware!

Features

  • Fast and Small 32-bit EXEs for Microsoft Windows 95/98/ME/NT/2000/XP

  • 32-bit protected mode code generation for maximum performance

  • COM automation: Create COM controller applications to control COM server applications and DLLs

  • STDIN and STDOUT for standard I/O and Web CGI programming

  • A complete graphics package for easy development of graphic presentations, splash screens and more.

  • Support for Windows only printers with the XPRINT statement and functions

  • Supports existing Line Printers, with PowerBASIC LPRINT statements and functions

  • A complete set of advanced string manipulation functions: VERIFY, REMOVE, REPLACE, EXTRACT, TALLY, REPEAT, , and many more

  • REGEXPR and REGREPL functions for regular expression search and replace

  • Array Sort and Scan, element Insert and Delete

  • MIN and MAX value Functions that work with both and data types

  • PEEK, POKE, PEEK$, POKE$ for direct memory access

  • and Indexed Pointers for direct memory access

  • Matrix operations: Init, Identity, Transposition, Inversion, scalar, and matrix math

  • 80-bit Extended-precision math

  • Register Variables for increased performance: up to six unique register variables: (2) or (4)

  • Unsigned Integer types: BYTE (8-bit), WORD (16-bit) and DWORD (32-bit)

  • Signed Integer types: INTEGER (16-bit), LONG (32-bit) and QUAD (64-bit)

  • Two Currency variable types

  • Native ASCIIZ (Nul-terminated) strings

  • User-Defined TYPEs and UNIONs

  • FIELD variables for file I/O.

  • Variant, GUID, and Object variables

  • Use of all available memory (up to 2 Gb) for arrays and dynamic strings

  • Optional parameters in BASIC Subs and Functions

  • Optional parameter passing to and procedures

  • Optional requirement that variables be declared before use

  • Built-in 32-bit Inline Assembler with 80486 and Pentium opcodes

  • Inline Assembler includes Floating-Point and MMX instructions

  • Import Subs and Functions from the entire Win32 or any 32-bit DLL

  • Multi-thread application support: Create, Suspend, Resume, Status, and Close

  • Client/Server Network Communications - TCP/UDP for E-mail, FTP, etc.

  • High-speed Serial Communications support

  • True 32-bit code pointers, great for callbacks

  • Easy to use syntax highlighting Integrated Development Environment (IDE) and debugger

PB/CC supports PRINT, LOCATE, LINE INPUT, INKEY$, INSTAT, CLS, COLOR, SCREEN and LPRINT. All of the standard text functions. But then we added more. CURSOR, INSHIFT, PAGE, PCOPY, WAITKEY$, WAITSTAT. A complete set of handlers. SCROLL any region of the screen in any direction. STDIN, STDOUT, and STDERR. Even PARSE$ and JOIN$ functions to extract and build delimited fields, and new options for the EXTRACT$ function, too. All the text tools you'll ever need!

See Also

What is PB/CC?

The Integrated Development Environment

Writing Programs in PBCC


What is PB/CC?

Ever wished for a 32-bit compiler? One with the straightforward DOS interface but Megabytes and Megabytes of memory? Ever wished for a 32-bit Windows compiler that's truly easy to use? Well, now it's here. Today, the PowerBASIC Console Compiler for Windows gives you all that and more. With PB/CC, it's a whole new Windows! Text mode applications for Win95/98/ME, WinNT, Windows 2000, and Windows XP!

The Win32 Console is a text mode interface connected right to the heart of 32-bit Windows. With a console application, there's no fluff, no animated puppets, just intense computing power. Port existing BASIC code from DOS to Windows today! Access a flat memory model that's practically unlimited. Boost performance with true 32-bit code. Launch multiple concurrent . Use the CPU and FPU directly with Inline Assembler. Choose Register Variables for your most critical code. With PB/CC, size and performance are the prime factors.

Did you say Internet? PB/CC offers the ability to easily create compact CGI applications using built-in Standard Input and Output features to communicate with Web Servers using the efficient POST method. A typical shopping cart program in Visual Basic needs some 5 Megabytes of RAM. So even with a 128 MB server, you may find that you are limited to around 25 simultaneous connections. Use PB/CC and memory needs plummet dramatically - you may be able to have over a thousand simultaneous connections on that very same Web Server!

But this is just the tip of the iceberg…

See Also

Introducing PB/CC 4.0 for Windows

The Integrated Development Environment

Writing Programs in PBCC


New Statements and Functions

  • #COMPILER metastatement defines the compiler for your program.

  • #CONSOLE metastatement lets you choose whether to create a console window when your program executes.

  • ARRAY ASSIGN statement allows the assignment of a number of values to successive elements of an array. The assignment always starts with the first array element, and continues sequentially as the elements appear in memory. The values to be assigned must match the array type, and may be literals, variables, or expressions. For example:

ARRAY ASSIGN x&() = 1,2,3,4,5,6,7,8,9,10

  • BGR function converts an RGB color value to BGR format.

  • BITS function condenses the functionality of BITS%, BITS&, BITS?, BITS??, and BITS??? into a single function. The BITS syntax provides for the addition of new data types in future versions of PowerBASIC, which may not support type-specifier characters. Existing code should be converted to the new syntax as soon as possible. Note that CINT, CLNG, CBYT, CWRD, and CDWD are functional synonyms for BITS.

  • CONSOLE GET LOC retrieves the location of the console window on the screen.

  • CONSOLE GET SCREEN retrieves the size of the visible part of the console screen, in text rows and columns.

  • CONSOLE GET SIZE retrieves the size of the console window, in pixels.

  • CONSOLE GET VIEW retrieves the position of the viewable part of a virtual screen.

  • CONSOLE GET VIRTUAL retrieves the size of the screen buffers associated with the console window.

  • CONSOLE SET FOCUS brings the console window to the foreground and directs the focus to it.

  • CONSOLE SET LOC moves the console window to a new location on the screen.

  • CONSOLE SET SCREEN change the size of the console window and its associated screen buffers.

  • CONSOLE SET VIEW sets the position of the viewable part of a virtual screen.

  • CONSOLE SET VIRTUAL changes the size of the screen buffers associated with the console window.

  • DECLARE statement allows declaring procedures with different names, but the same aliases.

  • DESKTOP GET CLIENT statement retrieves the size of the client area of the desktop, in pixels.

  • DESKTOP GET LOC statement retrieves the location of the top, left corner of the client area of the desktop, in pixels.

  • DESKTOP GET SIZE statement retrieves the size of the entire desktop, in pixels.

  • END terminates your program immediately.

  • ENVIRON statement modifies the environment table for the current process and any subsequent child processes that are launched. A single string expression parameter sets both the name of the variable and its value, delimited by an equal sign. If a value isn’t given, the variable is removed from the environment. For example:

ENVIRON "SETMODE=YES"

ENVIRON "SETMODE="

  • ERL system variable returns the last line number encountered before the most recent error, within the current Sub/Function. With ERL, line numbers are of the traditional-basic line numbering variety, not the physical source code line.

  • FIELD statement binds a field string variable to a random file buffer or a dynamic string variable. A field variable is defined to be a specific sub-section of a random file buffer or a dynamic string variable. Field string variables must be explicitly declared using DIM, LOCAL, STATIC, GLOBAL, or THREADED statements.

  • GRAPHIC ARC statement draws an arc in the selected graphic window.

  • GRAPHIC ATTACH statement selects the graphic target (window or bitmap) on which future drawing operations will take place.

  • GRAPHIC BITMAP END statement closes the selected graphic bitmap.

  • GRAPHIC BITMAP LOAD statement creates a memory bitmap and loads an image into it.

  • GRAPHIC BITMAP NEW statement creates a new memory bitmap.

  • GRAPHIC BOX statement draws a box with square or rounded corners in the selected graphic window.

  • GRAPHIC CHR SIZE statement retrieves the character size for the current font in the selected graphic window.

  • GRAPHIC CLEAR statement clears the entire selected graphic window, optionally using a specified color and fill style.

  • GRAPHIC COLOR statement sets the foreground color and optionally the background color for various graphic statements.

  • GRAPHIC COPY statement copies a bitmap to the selected graphic target.

  • GRAPHIC ELLIPSE statement draws an ellipse or a circle in the selected graphic window.

  • GRAPHIC FONT statement selects a font for the GRAPHIC PRINT statement.

  • GRAPHIC GET BITS statement retrieves a copy of a bitmap, storing it as a device-independent bitmap in a dynamic string variable.

  • GRAPHIC GET CLIENT statement retrieves the client size of the selected graphic target.

  • GRAPHIC GET DC statement retrieves the handle of the DC (device context) for the selected graphic window.

  • GRAPHIC GET LOC statement retrieves the location of the selected graphic window on the screen.

  • GRAPHIC GET MIX statement retrieves the color mix mode for the selected graphic target.

  • GRAPHIC GET PIXEL statement retrieves the color of the pixel at the specified point in the selected graphic window.

  • GRAPHIC GET POS statement retrieves the POS (last point referenced) by a graphic statement.

  • GRAPHIC GET PPI statement retrieves the resolution of the display device, in points per inch.

  • GRAPHIC LINE statement draws a line.

  • GRAPHIC PAINT statement fills an area with a solid color or a hatch pattern.

  • GRAPHIC PIE statement draws a pie section on the selected graphic target.

  • GRAPHIC POLYGON statement draws a polygon in the selected graphic target.

  • GRAPHIC POLYLINE statement draws a series of connected line segments.

  • GRAPHIC PRINT statement outputs text to the selected graphic target.

  • GRAPHIC REDRAW statement updates buffered graphical statements, drawing them to the selected graphic target.

  • GRAPHIC RENDER statement renders an image on the selected graphic target.

  • GRAPHIC SAVE statement saves an image to a bitmap (.BMP) file.

  • GRAPHIC SCALE statement defines a custom coordinate system for the graphic target.

  • GRAPHIC SET BITS statement replaces a copy of a bitmap that was retrieved as a device-independent bitmap.

  • GRAPHIC SET FOCUS statement brings the selected graphic window to the foreground and directs focus to it.

  • GRAPHIC SET LOC statement changes the location of the selected graphic window on the screen.

  • GRAPHIC SET MIX statement sets the color mix mode for the selected graphic target.

  • GRAPHIC SET PIXEL statement draws a single pixel.

  • GRAPHIC SET POS statement sets the last point referenced (POS) for the selected graphic target.

  • GRAPHIC STRETCH statement sets the last point referenced (POS) for the selected graphic target.

  • GRAPHIC STYLE statement sets the line style to be used by various graphical statements in the selected graphic target.

  • GRAPHIC TEXT SIZE statement calculates the size of text to be printed.

  • GRAPHIC WIDTH statement sets the line width to be used by various graphical statements in the selected graphic target.

  • GRAPHIC WINDOW statement creates a new graphic window.

  • GRAPHIC WINDOW END statement closes and destroys the selected graphic window.

  • HI function extracts the most significant (high-order) portion of an value. This supercedes the HIBYT, HIINT, and HIWRD functions although they remain supported for a limited period. Existing code should be converted to the new syntax as soon as possible.

  • INPUT statement reads a line from the keyboard, assigning data to one or more variables.

  • KEY statement assigns a string expression to a function key (F1 to F12).

  • KEY$ function retrieves the string expression assigned to a function key with the KEY statement.

  • LO function extracts the least significant (low-order) portion of an integer-class value. This supercedes the LOBYT, LOINT, and LOWRD functions although they remain supported for a limited period. Existing code should be converted to the new syntax as soon as possible.

  • LPRINT CLOSE statement detaches the currently selected printer from LPRINT operations and, if required, informs the spooler subsystem to commence print operations.

  • MAK function creates an integer-class value of a specified data type (WORD, DWORD, , INTEGER, LONG, QUAD) from a low-order and a high-order part. MAK supercedes the MAKWRD, MAKDWD, and MAKPTR function although they remain supported for a limited period. Existing code should be converted to the new syntax as soon as possible.

  • NUL$ function returns a string consisting of a specified number of $NUL (CHR$(0)) characters.

  • PRINTER$ function retrieves printer names and printer port names.

  • PRINTERCOUNT function retrieves the number of available (installed) printers.

  • THREADED statement declares one or more Thread Local Storage (TLS) variables. It also, optionally, accepts a list of variables, all of which are defined by the type descriptor keywords which follow them. For example:

THREADED xxx, yyy, zzz AS INTEGER

THREADED vptr, aptr() AS LONG PTR

  • XPRINT statement outputs text to a host printer device.

  • XPRINT ARC statement draws an arc on a host printer page.

  • XPRINT ATTACH statement connects a host-based (GDI) printer for use with XPRINT.

  • XPRINT BOX statement draws a box with square or rounded corners on a host printer page.

  • XPRINT CANCEL statement deletes the current print job and detaches the host printer.

  • XPRINT CHR SIZE statement retrieves the character size for the current font on the host printer.

  • XPRINT CLOSE statement detaches a host printer so printing may begin.

  • XPRINT COLOR statement sets the foreground color (and, optionally, the background color) for various XPRINT statements.

  • XPRINT COPY statement copies a to a host printer page.

  • XPRINT ELLIPSE statement draws an ellipse or a circle on a host printer page.

  • XPRINT FONT statement selects a font to be used by the XPRINT statement.

  • XPRINT FORMFEED statement starts a new page for the host printer.

  • XPRINT GET CLIENT statement retrieves the client size of the host printer page.

  • XPRINT GET DC statement retrieves the handle of the Device Context for the host printer.

  • XPRINT GET LINES statement retrieves the number of lines that can be printed.

  • XPRINT GET MARGIN statement retrieves the margin sizes for the host printer.

  • XPRINT GET MIX statement retrieves the color mix mode for the host printer page.

  • XPRINT GET ORIENTATION statement retrieves the page orientation for the host printer page.

  • XPRINT GET PIXEL statement retrieves the color of a pixel on a host printer page.

  • XPRINT GET POS statement retrieves the last point referenced (POS) by an XPRINT statement.

  • XPRINT GET PPI statement retrieves the resolution of the host printer page.

  • XPRINT GET QUALITY statement retrieves the print quality setting for a host printer.

  • XPRINT GET SIZE statement retrieves the total size of the host printer page.

  • XPRINT LINE statement draws a line on a host printer page.

  • XPRINT PIE statement draws a pie section on a host printer page.

  • XPRINT POLYGON statement draws a polygon on a host printer page.

  • XPRINT POLYLINE statement draws a series of connected lines on a host printer page.

  • XPRINT RENDER statement renders an image on a host printer page.

  • XPRINT SCALE statement defines a custom world coordinate system for the host printer page.

  • XPRINT SET MIX statement sets the color mix mode for the host printer page.

  • XPRINT SET ORIENTATION statement sets the page orientation for the host printer page.

  • XPRINT SET PIXEL statement sets the color of a pixel on a host printer page.

  • XPRINT SET POS statement sets the last point referenced (POS) by an XPRINT statement.

  • XPRINT SET QUALITY statement sets the print quality for a host printer.

  • XPRINT STRETCH statement copies and resizes a bitmap to the host printer page.

  • XPRINT STYLE statement sets the line style to be used by various XPRINT statements.

  • XPRINT TEXT SIZE statement calculates the size of text to be printed on a host printer.

  • XPRINT WIDTH statement sets the graphic line width to be used by various XPRINT statements.

  • XPRINT$ function returns the name of the attached host printer.

See Also

Changes to existing Statements and Functions

New in the IDE

Additonal Changes

Previous changes (PB/CC 3.0 release)


Changes to existing Statements and Functions

  • #COMPILE metastatement may be used to specify an alternate name for the compiled program and the target program type. The two program type choices are EXE (Executable) and DLL (Dynamic Link Library). If no program type is specified, a default of EXE is presumed. For example:

#COMPILE [EXE|DLL] "newname.exe"

  • CASE statement may now contain an IS keyword in a SELECT block. For example:

CASE IS 42&

  • CONSOLE SCREEN has been deprecated. Switch to using CONSOLE SET SCREEN as soon as possible.

  • DATA statement parsing rules have been revised, as follows:

    • Data items must be separated by commas.

    • Use of quoted data is highly recommended, as all characters within quotes are accepted without special treatment. The quote character itself may be represented within quoted data by two quote characters.

    • The only special characters recognized outside of quoted data are the comma (data separator) and colon (statement separator). Underscore (line continuation), apostrophe (comment), and REM (remark) are treated as any other characters, and included as a part of the data item.

    • Unquoted data is trimmed of all leading and trailing spaces and tabs.

    • To add a remark after DATA, use a colon as a statement separator, followed by apostrophe/REM. For example:

DATA "This data" : REM Some Text

    • Previous statements regarding colon and underscore behavior no longer apply.

  • DEFINT, DEFSTR, etc. may appear anywhere in a program.

  • ERASE statement causes an array to be deactivated and its data area is released. Any memory assigned to the individual elements (if they are dynamic strings, Objects, Variants, etc.) is also released. Operation is not affected by the scope (LOCAL, STATIC, etc.) of the data. LOCAL arrays are implicitly erased upon exit from the Sub/Function.

Absolute arrays (DIM...AT) are the sole exception -- an explicit ERASE will release the individual elements, if needed, but the full data block is left as-is because no assumptions can be made as to its origin. It is the programmer's responsibility to ensure it is handled correctly. In an implied ERASE (Sub/Function exit) of a LOCAL absolute array, the array descriptor is deactivated, but no changes of any kind are made to the individual data elements or the full block. RESET may be used to set arrays to zeroes or empty strings without releasing the data block.

  • FILEATTR(enum,3) enumerates existing file numbers, in the range of 1 to 32767. FILEATTR(1,3) returns the first located file number, FILEATTR(2,3) the second, and so on until -1 is returned to indicate that there are no more file numbers active. The file numbers returned are not guaranteed to be returned in any particular sequence, nor be open. You can use FILEATTR(#filenum,0) to determine whether a given file number is open or closed.

  • GET statement, when used without a target variable, reads file data to an internal file buffer. The data in the buffer may then be accessed using FIELD string variables.

  • GLOBAL statement may now optionally accept a list of variables, all of which are defined by the type descriptor keywords which follow them. For example:

GLOBAL aaa, bbb, ccc AS INTEGER

GLOBAL vptr, aptr() AS LONG PTR

  • GRAPHIC WINDOW updates now execute faster.

  • The IF statement and IF/END IF block no longer support an optional comma before the THEN clause.

  • JOIN$ function has been enhanced with a BINARY option. If the array consists of fixed size elements ( , ASCIIZ, etc.), the returned consists of an exact memory image of the array data in internal format. If the array contains variable length data (Dynamic string, Field string), it is stored in PowerBASIC and/or Visual Basic packed string format: If a string is shorter than 65535 bytes, it starts with a 2-byte length WORD followed by the string data. Otherwise it will start a 2-byte value of 65535, followed by a DWORD indicating the string length, then finally the string data itself.

  • LCASE$, MCASE$, and UCASE$ statements now have an optional parameter to specify whether to use the Console's OEM charset or the systems ANSI charset when converting a string expression to another case.

  • LEFT$ function may take a negative length value as its second parameter. If the length value parameter is negative, it is interpreted as LEN(LeftExpr$)-ABS(LenParam). For example

LEFT$("1234567890",-8) returns "12".

  • LET statement (explicit or implied) now includes all the functionality of the SET statement in prior versions of PowerBASIC. While SET remains supported for assignment with Object variables, existing SET code should be converted to the new syntax as soon as possible. This simply involves deleting the word SET.

LET may now be used to assign an Object reference to an Object variable or a Variant variable. After an Object variable is declared, a valid Object reference must be assigned to it before the Object's methods and/or properties can be used.

  • LOCAL statement may now optionally accept a list of variables, all of which are defined by the type descriptor keywords which follow them. For example:

LOCAL aaa, bbb, ccc AS INTEGER

LOCAL vptr, aptr() AS LONG PTR

  • MID$ function may take a negative length value as the third parameter. If the length value parameter is negative, it is interpreted as LEN(MidExpr$)-ABS(LenParam). For example, MID$("1234567890",3,-3) returns "3456789".

  • MID$ statement may take a negative length value as the third parameter. If the length value parameter is negative, it is interpreted as LEN(MidExpr$)-ABS(LenParam). For example:

MID$("ABCDEFGHIJK",3,-7) = "*******" yields "AB****GHIJK".

  • PARSE$ function has been enhanced with a BINARY option. It presumes that the string expression was created with the JOIN$/BINARY function, or its equivalent, which creates a string as a binary image or in PowerBASIC and/or Visual Basic packed string format: If a string is shorter than 65535 bytes, it starts with a 2-byte length WORD followed by the string data. Otherwise it will start a 2-byte value of 65535, followed by a DWORD indicating the string length, then finally the string data itself.

  • PARSE statement has been enhanced with a BINARY option. It presumes that the string expression was created with the JOIN$/BINARY function, or its equivalent, which creates a string as a binary image or in the PowerBASIC and/or Visual Basic packed string format: If a string is shorter than 65535 bytes, it starts with a 2-byte length WORD followed by the string data. Otherwise it will start a 2-byte value of 65535, followed by a DWORD indicating the string length, then finally the string data itself.

  • PARSECOUNT function has been enhanced to add a BINARY option, which returns the number of sub-fields found. It presumes that the string expression was created with the JOIN$/BINARY function, or its equivalent, which creates a string in the PowerBASIC and/or Visual Basic packed string format: If a string is shorter than 65535 bytes, it starts with a 2-byte length WORD followed by the string data. Otherwise it will start a 2-byte value of 65535, followed by a DWORD indicating the string length, then finally the string data itself.

  • PEEK function now offers the functionality of a dynamic pointer for any numeric data type. If the data type is not specified, it defaults to BYTE to maintain compatibility with prior versions of PowerBASIC.

  • PEEK$ function has been enhanced with an ASCIIZ option. This reads successive characters from an ASCIIZ buffer of a specified size, until a terminating $NUL (CHR$(0)) byte is found. Since ASCIIZ strings must contain a terminating $NUL, the maximum length of the returned string is 1 character less than the specified size.

  • POKE function now offers the functionality of a dynamic pointer for any numeric data type. If the data type is not specified, it defaults to BYTE to maintain compatibility with prior versions of PowerBASIC.

  • POKE$ function has been enhanced with an ASCIIZ option. This writes successive characters to a target address in memory until a terminating $NUL byte is found in the source string. If no $NUL is found in the string, one is automatically appended to the target buffer. It is the programmer’s responsibility to ensure that POKE$ does not overrun the target memory area to avoid data corruption or protection faults.

  • PUT statement, when used without a target variable, writes file data from an internal file buffer. Data must first be assigned to the file buffer using FIELD string variables.

  • RESET statement may now accept multiple parameters, each delimited by a comma. For example:

RESET a&, b$(5), c#(), d##()

  • RESUME and RESUME NEXT may be used within an error handling routine to transfer control to the statement which caused the error, or the next statement. If RESUME is executed with no preceding error, execution continues after the RESUME. ON ERROR and RESUME may not be used within a TRY/END TRY block..

  • RIGHT$ function may take a negative length value as its second parameter. If the length value parameter is negative, it is interpreted as LEN(RightExpr$)-ABS(LenParam). For example:

RIGHT$("1234567890",-8) returns "90".

  • SHELL statement and SHELL function have been enhanced with the HANDLES keyword, which allows the target application (process) to inherit its operating system handles from the program launching the SHELL operation.

  • STATIC statement may now optionally accept a list of variables, all of which are defined by the type descriptor keywords which follow them. For example:

STATIC aaa, bbb, ccc AS INTEGER

STATIC vptr, aptr() AS LONG PTR

  • TYPE and UNION structures may contain bit variables, which are named BIT (unsigned values) or SBIT (signed values). Each bit variable may occupy from 1 to 31 bits, and they may be packed one after another up to a total of 32 bits per bit field. The size of a bit variable is defined as follows:

var AS BIT * nlit [IN BYTE|WORD|DWORD]

…where the term "* nlit" defines the number of bits (1 to 31), and the optional term "IN BYTE|WORD|DWORD", if present, defines the start of a new bit field of 1, 2, or 4 bytes. For example:

TYPE ABCD

Valu2 AS BIT * 31 IN DWORD

Sign1 AS SBIT * 1

nybl2 AS BIT * 4 IN BYTE

nybl1 AS BIT * 4

END TYPE

The example TYPE structure above is 5 bytes in size, containing a 4-byte bit field and a 1-byte bit field. In this case, each contains two bit-variables of varying size. The range of values which may be stored depends upon the number of bits available. For example, "BIT * 4" has a range of 0 to 15, "SBIT * 1" has a range of -1 to 0, and "SBIT * 5" has a range of -16 to +15.

  • USING$ function with alternate fill characters "**#" requires a minimum of a 3-byte field.

See Also

New Statements and Functions

New in the IDE

Additonal Changes

Previous changes (PB/CC 3.0 release)


Additonal Changes

  • The compiler has been updated to a 32-bit application, to assure compatibility with 64-bit versions of Windows.

  • A new data type has been added, named FIELD. A FIELD string is a special form of dynamic string, which has all the capabilities of a dynamic string, but may also represent a defined portion of a Random-access file buffer, or a defined portion of a dynamic string. FIELD strings must always be declared using DIM, LOCAL, STATIC, GLOBAL, or THREADED statements. FIELD strings occupy sixteen bytes more memory, and incur slightly more overhead than a standard dynamic string.

  • Additional predefined numeric equates are included to represent each of the new PowerBASIC intrinsic data types. At this time, the primary purpose is for use with the ARRAYATTR function. Use these equates instead of actual numeric values, as the numbers will mean different things in future versions of PowerBASIC. Available equates include:

%VARCLASS_BIT (UDT unsigned bit member)

%VARCLASS_SBT (UDT signed bit member)

%VARCLASS_FLD (FIELD string variable)

  • Local Storage (TLS) is supported through a new variable scope definition named THREADED. These variables are visible to all functions, but each thread is allocated its own unique set of them. Therefore, data can be shared between functions on a thread by thread basis. All allocation and deallocation of THREADED memory is handled automatically by PowerBASIC without need for additional handling by the programmer.

By their nature, THREADED variables can simplify thread-safe programming; however, they impose a slight (although measurable) performance penalty compared to other variable types. THREADED variables are allocated with the THREADED statement, or by a THREADED attribute to the DIM statement. For example:

DIM abc AS THREADED STRING

THREADED aaa, bbb, ccc, ddd AS VARIANT

  • DATA statements in prior versions ignored any plain text which was located immediately after a quoted literal up to the next comma or end-of-line. This condition now generates an Error 477 ("Syntax error").

  • Error 99 added: "Object error".

  • Error 241 altered: "Global memory corrupt".

  • Error 408 added: "Wrong compiler for this program".

  • Error 409 added: "Sub/Function is too large"

  • Error 431 altered: "Numeric scalar variable expected" is generated when a For/Next counter variable is a BYREF param, a pointer target, a THREADED variable, an array variable, or is not numeric.

  • Error 438 added: "Member name expected".

  • Error 464 has been deleted and merged with Error 481.

  • Error 480 altered: "Parameter mismatches definition" is generated when a parameter used in a call to a procedure does not match (or cannot be converted to) the data type or reference type (BYREF, BYVAL, BYCOPY) found in the procedure declaration/definition.

  • Error 481 altered: "Mismatch with prior definition" is generated when a program element (User Type, Union, Function, Macro, etc.) implementation mismatches a previous definition. It could be a Function which mismatches a declaration, a duplicate interface/type/etc. which is not identical, or another similar condition.

  • Error 484 altered: "Requires Procedure (Function/Sub...)".

  • Error 510 added: "Interface name expected".

  • Error 511 added: "Numeric operand expected".

  • Error 530 altered: "Invalid member name/definition" is generated for a variety of errors involving members of Types, Unions, and Interfaces. For example, a structure with no members, an invalid name, or an attempt to embed a disallowed data type, such as a Variant.

  • Error 534 added: "Bit field error".

  • Error 544 added: "Field variable expected".

  • Error 555 altered: "ON ERROR / RESUME not allowed here".

  • Error 565 added: "Macro mismatch with code position".

  • Note that using invalid target values with CALL DWORD, GOTO DWORD, and GOSUB DWORD statements will cause undefined results.

  • Members of User-Defined TYPEs and UNIONs can now use PowerBASIC reserved words as names. The single exception is END, which must remain reserved in order to terminate the structure declaration. So, for example, MyType.Print may be a valid data reference.

  • Fixed length strings, ASCIIZ strings, TYPEs and UNIONS may now be used as OPTIONAL or parameters.

  • When a function is declared to have a parameter of a type, PowerBASIC has construed the parameter as being passed BYVAL, instead of the typical default of BYREF. PowerBASIC now strictly enforces the rule that the parameter be declared BYVAL, so that default BYREF behavior may be supported in a future release.

  • MACRO references are now tested for correct source code position. Multi-line statement macros are only allowed in the statement position (line start or immediately following a colon). Function macros are never allowed in the statement position. Any failure generates an Error 565 ("Macro mismatch with code position").

  • SET statement is now obsolete. All of its functionality has been assumed by LET, or its implied form.

See Also

New Statements and Functions

Changes to existing Statements and Functions

Previous changes (PB/CC 3.0 release)


New in the IDE

  • The debugger has been updated to support 32-bit symbol tables, assuring compatibility with 64-bit versions of Windows.

  • Custom help files may be added to the IDE, with full context-sensitive keyword support.

  • New keyboard shortcuts, CTRL+ALT+F1 to display the Table of Contents tab of the PowerBASIC For Windows Help file, CTRL+ALT+F2 to display the Index tab of the PowerBASIC For Windows Help file, and CTRL+ALT+F3 to display the Search tab of the PowerBASIC For Windows Help file.

  • File templates allow you to start writing new programs with all the boilerplate already filled in for you. Sample templates, like "Generic PB program" and "Resource file with version info", are provided. You can customize these or create your own templates from scratch.

  • A selection of block operations has been added, allowing you to indent or outdent a block of lines. Blocks can also be commented out (or back in).

  • Bookmarks have been added, so you can mark your place in a file and, later, jump back to it.

  • The debugger has been integrated into the main IDE. Among other things, this allows you to set breakpoints in any loaded file, at edit time or debug time.

  • The debugger can be set to halt execution as soon as an error is detected (ERR <> 0). This option can be selected in the Compiler Preferences dialog. For best results, add #DEBUG ERROR ON to your program when using this option.

  • The debugger now supports the use of , SHELL functions, and SHELL statements in your program.

  • The IDE will not attempt to compile a file unless the file extension matches a recognized main module extension, such as .BAS or .RC. You can customize the file extension list in the Editor Preferences dialog.

  • The colors for bookmarks, breakpoints, and the execution point may be changed in the Color Preferences dialog.

  • Toolbar icons have been added for Print, Toggle Variable Watcher, Toggle Register Watcher, and Exit Debugger. The New File and Open File icons now include extra functionality through drop-down menus.

  • Files may be saved using tab compression. As well as reducing the size of your files, this helps preserve your indentation levels when sharing files with others who use different tab sizes. Tab compression may be selected in the File Preferences dialog.

  • IDE feedback such as compiler results and debug messages now go to a scrolling output window and, optionally, to an improved message box. The output window can be cleared or copied to the clipboard via a context menu. The context menu can also help track down compile-time errors, by letting you jump to the line containing the error and bring up context-sensitive help for the error.

See Also

New Statements and Functions

Changes to existing Statements and Functions

Additonal Changes

Previous changes (PB/CC 3.0 release)


Previous changes (PB/CC 3.0 release)

  • #BLOAT metastatement. Artificially inflate the disk image size of a compiled program.

  • #STACK metastatement. Specify the maximum potential stack size of an application.

  • #TOOLS metastatement. Enable or disable code generation for integrated development tools such as TRACE, PROFILE, and CALLSTK.

  • ACODE$ function. Translate a Unicode string into the equivalent ANSI .

  • ARRAYATTR function. Return various descriptive attributes of an array.

  • BIT CALC statement. Set or reset a bit based on the result of a calculation.

  • CALLSTK statement. Save a "how did I get here" list of Subs/Functions and their parameters (stack frames) that comprise the call stack at a given point of execution in a compiled program.

  • CALLSTK$ function. Retrieve the details of a specific stack frame from the call stack.

  • CALLSTKCOUNT function. Retrieve the number of stack frames in the call stack. Used in conjunction with the CALLSTK$ function above.

  • CHOOSE function. Return one of several values, based upon the value of an index. CHOOSE offers and modes, plus a Long-integer optimized mode.

  • CLSID$ function. Return a 16-byte CLSID string (128-bit GUID format string) associated with a unique ProgramID string of a COM object or component.

  • CONSHNDL function. Return the window handle of the console window.

  • CSET statement. Center a string within the space of another string or User-Defined Type.

  • CSET$ function. Return a string containing a centered (padded) string.

  • DIR$ CLOSE statement. Force the release of the operating system FindNext handle.

  • ERROR$ function. Return a string containing the descriptive name of a specified PowerBASIC run-time error code.

  • FILESCAN statement. Rapidly scan a file opened for INPUT or BINARY mode, in order to obtain size information about variable length string data.

  • FUNCNAME$ function. Return the name of the current Sub/Function.

  • GETSTDKBD function. Return the handle of the keyboard device, which may differ from GETSTDIN if redirection is in effect.

  • GETSTDVID function. Return the handle of a specified console video page.

  • GUID$ function. Return a 16-byte (128-bit) binary Globally Unique Identifier (GUID/UUID) from a human-readable GUID/UUID string, in the format "{01234567-89AB-CDEF-FEDC-BA9876543210}". GUID$ can also be used in string equate assignments.

  • GUIDTXT$ function. Return a 38-byte human-readable Globally Unique Identifier (GUID/UUID) in the format "{01234567-89AB-CDEF-FEDC-BA9876543210}".

  • HIINT function. Extract the most significant (high-order) Word from a Long-integer or Double-word (DWORD) value, and returns it as a signed Integer value.

  • IIF function. Return one of two values based on a TRUE/FALSE evaluation of an expression.

  • INTERFACE/END INTERFACE block. Declare a dispatch interface and its member Methods/Properties for the purposes of early-binding to a COM interface.

  • ISNOTHING function. Determine the current status of a given object variable. Complement to the ISOBJECT function.

  • ISOBJECT function. Determine the current status of a given object variable. Complement to the ISNOTHING function.

  • JOIN$ function. Return a string consisting of all of the strings in a string array, each separated by a delimiter.

  • LOINT function. Extract the least significant (low-order) Word from a Long-integer or Double-word (DWORD) value, and return it as a signed Integer value.

  • LSET$ function. Return a string containing a left-justified (padded) string. Also refer to CSET$ and RSET$.

  • MACRO/END MACRO block. Macro is a powerful text substitution construct which may take a single-line or multi-line form.

  • MAKINT function. Take the low-order byte from each of two variables and combine to produce a signed 16-bit Integer value.

  • MAKWRD function. Take the low-order byte from each of two integer-class variables and combine to produce an unsigned 16-bit WORD value.

  • OBJACTIVE function. Return TRUE/FALSE as an indication of the running state of an initialized COM object.

  • OBJECT statement. Communicate with a COM object through the dispatch interface.

  • OBJPTR function. Return an object contained in the specified object variable.

  • OBJRESULT function. Return the result of the execution of the most recent OBJECT statement.

  • PAGEACTIVE function. The PAGEACTIVE function returns the current active console page number in the range 1 to 8 inclusive.

  • PAGEVISIBLE function. The PAGEVISIBLE function returns the current visible console page number in the range 1 to 8 inclusive.

  • PARSE statement. Parse an entire string and extract all delimited fields into an array.

  • PROFILE statement. Capture a profile detailing total execution times of the Subs and Functions in a program.

  • PROGID$ function. Return the unique alphanumeric PROGID string (text) associated with a unique CLSID string of a COM object or component.

  • RETAIN$ function. Return a string containing only the characters contained in a specified match string. All other characters are removed.

  • RSET$ function. Return a string containing a right-justified (padded) string. Also refer to CSET$ and LSET$.

  • SET statement. Assign a Dispatch Interface reference to an object variable in preparation to use a COM interface.

  • SWITCH function. Return one of a series of values based upon a TRUE/FALSE evaluation of a corresponding series of expressions.

  • TAB$ function. Return a string with embedded TAB ($TAB) characters expanded with spaces to a given tab stop.

  • THREADCOUNT function. Return the number of active threads that exist in a module.

  • TRACE statement. Capture a representation of the precise flow of execution in a module

  • TRY/END TRY block. A structured method of trapping and responding to run-time errors.

  • TYPE SET statement. Assign the value of a User-Defined Type or string expression into another User-Defined Type or string variable.

  • UCODE$ function. Translate an ANSI string into the equivalent Unicode string.

  • USING$ function. Format one or more string or numeric expressions, based upon the contents of the format mask string. USING$ offers the same functionality as the USING statement of some DOS BASIC's.

  • VARIANT# function. Return the numeric value contained in a Variant variable.

  • VARIANT$ function. Return the dynamic string value contained in a Variant variable.

  • VARIANTVT function. Determine the internal of the data stored in a Variant variable.

  • #INCLUDE metastatement. PowerBASIC supports up to six levels of nested files (including the primary file), up from four levels in previous versions. Note that macros count as #include files for nesting purposes.

  • CHR$ function. CHR$ accepts character ranges and string expressions. For example, CHR$($DQ, 65 TO 90, "Hello!", 13, 10). CHR$ returns an empty string if the argument is -1. For example, CHR$(65,-1,66) returns "AB". This complements the operation of ASC on an empty string.

  • COMM LINE statement. COMM LINE sets the state of EOF to TRUE (non-zero) if an incomplete line of text was received by the COMM LINE statement.

  • DIM statement. The DIM statement may now be used to declare VARIANT and DISPATCH variables.

  • EOF function. The EOF function may now be used to detect if an incomplete line of text was received by the COMM LINE and TCP LINE statements.

  • GET statement. The GET statement can now be used to read an entire array from a file opened in BINARY mode. Also refer to the FILESCAN, PUT and LINE INPUT# statements.

  • LET statement. The LET statement has been extended to allow 6 different forms of assignment to or from Variant variables.

  • LINE INPUT statement. The LINE INPUT statement can now accept a string equate or a string literal for the optional prompt parameter. LINE INPUT now accepts up to 9999 characters (previous limit was 128 characters).

  • LINE INPUT# statement. The LINE INPUT# statement can now be used to read an entire sequential disk file into a string array. Also refer to the FILESCAN, PRINT#, PUT and GET statements.

  • LSET statement. The LSET statement has been extended to provide an optional padding character, in line with the new CSET statement. Also refer to the RSET statement.

  • ON GOTO and ON GOSUB statements. These have both been revised to offer even greater performance.

  • PRINT# statement. The PRINT # statement can now be used to write an entire array to a sequential disk file. Also refer to the FILESCAN, LINE INPUT# and PUT and GET statements.

  • PUT statement. The PUT statement can now be used to write an entire array to a file opened in BINARY mode. Also refer to the FILESCAN, GET and LINE INPUT# statements.

  • REGEXPR function. The length& result parameter is now optional.

  • RSET statement. The RSET statement has been extended to provide an optional padding character, in line with the new CSET statement. Also refer to the LSET statement.

  • SCREENATTR statement. SCREENATTR can now return the current character attributes (foreground and background colors) as set for subsequent PRINT statements by the COLOR statement.

  • SELECT CASE block. The SELECT CASE block now offers additional performance enhancing options.

  • TCP LINE statement. TCP LINE sets the state of EOF to TRUE (non-zero) if an incomplete line of text was received by the TCP LINE statement.

  • TYPE/END TYPE block. The TYPE/END TYPE block now includes QWORD alignment, and FILL packing options. Combining DWORD and FILL duplicates Visual Basic's "natural" UDT alignment scheme.

  • The capacity for programs with larger code and/or data size has been substantially increased.

  • The compiler now supports a new command-line option, /C, which signifies the name of a file that contains the complete command-line of up to 1024 bytes, which may otherwise exceed the operating system command-line length limits.

  • Programs launched from the IDE can query the environment variable "PBIDE" with ENVIRON$ to determine if the program is running in the IDE. Similarly, the "PBDEBUG" environment variable is set when the program is running in the debugger. In both cases, the string will contain the IDE name and version number. For example, "CCEDIT 4.00".

  • PowerBASIC preserves the FPU precision state during calls to DLL functions.

  • Control codes in strings displayed by #DEBUG PRINT are translated into hex format in the output window. For example, embedded CHR$(0) or $NUL bytes are displayed as "<00>".

  • $SPC added to the list of predefined string equates.

  • Sub/Function parameters may now include Fixed Strings, ASCIIZ Strings, and User-Defined Types (UDTs) which are passed BYVAL. This capability should normally be limited to smaller sizes because of the overhead of copying data to the stack, and there is an absolute limit of 64 Kb for all parameters in one function combined (as imposed by the Intel CPU design). You may interchange any of these three parameter types, and still maintain maximum efficiency, as long as the length of the variable is identical to that of the parameter. You may also pass a dynamic string, a string expression, or a variable of a different length, but that will impose at least a small additional overhead.

  • Subs and Functions may now declare and define up to 32 parameters each. Previous versions of PowerBASIC were limited to 16 parameters.

  • Assignment of a value to a String Equate may now include a calculated string expression. This includes concatenation of string literals, other string equates, and the CHR$, STRING$, SPACE$, and the GUID$ functions. For example:

$ThisIsAnEquate = "More " & CHR$(9) & " flexible" & CHR$(13, 10, SPACE$(10))

$AppGuid = GUID$("{01234567-89AB-CDEF-FEDC-BA9876543210}")

$AtoZ = CHR$(65 TO 90)

  • Expressions involving Numeric Equates and conditional compilation may include the arithmetic operators +, -, *, /, and \, and the relational operators >,<,>=,<=,<>,=. For example:

%xyz = (%abc = 3)

#IF %abc = 77

  • Expressions involving Numeric Equates and conditional compilation may include the CVQ function. This allows you to easily assign numeric values to an equate, based upon a meaningful mnemonic. In this context, the CVQ expression is limited to a length of eight bytes. For example:

%Mode = CVQ("DEBUG")

%Style = CVQ("Cool")

  • User-Defined Type (UDT) or Union definitions may be repeated any number of times within source code, as long as the definitions are identical.

  • Structures (TYPE/UNION) may be embedded within another structure, for simplification in referencing deeply nested items, by simply stating the structure name alone at the appropriate position. The internal alignment of the member structure is precisely maintained regardless of other alignment specifications, to foster inheritance issues. For example:

TYPE ABCD3 TYPE ABCD2 UNION ABCD1

A AS LONG D AS DWORD F AS DWORD

ABCD2 E AS DOUBLE G AS LONG

C AS LONG ABCD1 H AS SINGLE

END TYPE END TYPE END UNION

In this case, you could access the lone Single-precision float member of this structure very simply. Assuming DIM X AS ABCD3, you could reference the Single-precision Union member with the statement X.H, instead of the extended syntax X.ABCD2.ABCD1.H

  • The Inline-assembler calculates string literals in reverse sequence in keeping with standard assembler behavior.

  • GUID variables are supported, and are used to contain a 128-bit Globally Unique Identifier, primarily for use with Com Objects. Generally speaking, a GUID variable is assigned a value with the GUID$() function, or with a string equate, and that value usually remains constant throughout the program. The GUID variable is typically used only as a parameter, rather than as a term in an expression.

GUID variables must be explicitly declared with DIM, LOCAL, etc., and are used in much the same way as a 16-byte fixed-length string or a User-Defined Type of that size. A GUID variable is only valid as a parameter when it contains precisely 16 bytes of data in an appropriate format. For example:

$idNull = GUID$("{00000000-0000-0000-0000-000000000000}")

...

DIM abc AS LOCAL GUID

DIM xyz AS GLOBAL GUID

abc = $idNull

abc = GUID$("{00000000-0000-0000-C000-000000000046}")

xyz = abc

programid$ = GUIDTXT$(xyz)

  • Variant variables are supported, but their use is limited to that of a parameter, assignment for conversion of data, and compatibility with other languages and applications. Although notoriously lacking in efficiency, Variants are commonly used as Com Object parameters due to their flexibility. You can think of a Variant as a kind of container, which can hold a variable of most any data type, numeric, string, or even an entire array. This simplifies the process of calling procedures in a Com Object Server, as there is little need to worry about the myriad of possible data types for each parameter.

This flexibility comes at a great price in performance, so PowerBASIC limits their use to data storage and parameters only. You may assign a numeric value, a string value, or even an entire array to a Variant with the LET statement, or its implied equivalent. In the same way, you may assign one Variant value to another Variant variable, or even assign an array contained in a Variant to a compatible PowerBASIC array, or the reverse.

You may extract a simple scalar value from a Variant with the VARIANT# function for numeric values (regardless of the internal numeric data type), or with the VARIANT$ function for string values. You may determine the type of data a Variant variable contains with the VARIANTVT function.

You may not use a Variant in an expression, print it, or name it as a member of a structure like a Type/Union, etc. Instead, you must first extract the value with one of the above conversion functions, and use that acquired value for calculations. A Variant is always 16 bytes in size, and may be passed as either a or a parameter, at the programmer's discretion.

However, when a BYREF Variant is required as a parameter, only an explicit Variant variable may be passed by the calling code - a BYCOPY expression is not allowed. All dynamic strings contained in a Variant must be Wide/Unicode, and PowerBASIC handles these conversions automatically. Every Variant variable must be explicitly declared with an appropriate statement such as:

DIM xyz AS VARIANT

or:

LOCAL xyz AS VARIANT

  • Additional predefined numeric equates are included to represent each of the PowerBASIC intrinsic data types. At this time, the primary purpose is for use with the ARRAYATTR function. You should always use these predefined equates, rather than their literal values, as the equated values are guaranteed to change in future versions of PowerBASIC. They include:

%VARCLASS_BYT

%VARCLASS_WRD

%VARCLASS_DWD

%VARCLASS_INT

%VARCLASS_LNG

%VARCLASS_QUD

%VARCLASS_SNG

%VARCLASS_DBL

%VARCLASS_EXT

%VARCLASS_CUR

%VARCLASS_CUX

%VARCLASS_VRNT

%VARCLASS_IFAC

%VARCLASS_GUID

%VARCLASS_TYPE

%VARCLASS_ASC

%VARCLASS_FIX

%VARCLASS_STR

  • Additional predefined numeric equates are included to represent each of the PowerBASIC Variant data types as returned by VARIANTVT. These identifiers include:

%VT_EMPTY

%VT_NULL

%VT_I2

%VT_I4

%VT_R4

%VT_R8

%VT_CY

%VT_DATE

%VT_BSTR

%VT_DISPATCH

%VT_ERROR

%VT_BOOL

%VT_VARIANT

%VT_UNKNOWN

%VT_I1

%VT_UI1

%VT_UI2

%VT_UI4

%VT_I8

%VT_UI8

%VT_INT

%VT_UINT

%VT_VOID

%VT_HRESULT

%VT_PTR

%VT_SAFEARRAY

%VT_CARRAY

%VT_USERDEFINED

%VT_LPSTR

%VT_LPWSTR

%VT_FILETIME

%VT_BLOB

%VT_STREAM

%VT_STORAGE

%VT_STREAMED_OBJECT

%VT_STORED_OBJECT

%VT_BLOB_OBJECT

%VT_CF

%VT_CLSID

%VT_VECTOR

%VT_ARRAY

%VT_BYREF

  • Additional predefined (built-in) numeric equates are included to represent each of the PowerBASIC Run-time Error Codes as returned by ERR and ERRCLEAR, etc. They include:

%ERR_NOERROR

%ERR_ILLEGALFUNCTIONCALL

%ERR_OVERFLOW (reserved)

%ERR_OUTOFMEMORY

%ERR_SUBSCRIPTPOINTEROUTOFRANGE

%ERR_DIVISIONBYZERO (reserved)

%ERR_DEVICETIMEOUT

%ERR_INTERNALERROR

%ERR_BADFILENAMEORNUMBER

%ERR_FILENOTFOUND

%ERR_BADFILEMODE

%ERR_FILEISOPEN

%ERR_DEVICEIOERROR

%ERR_FILEALREADYEXISTS

%ERR_DISKFULL

%ERR_INPUTPASTEND

%ERR_BADRECORDNUMBER

%ERR_BADFILENAME

%ERR_TOOMANYFILES

%ERR_DEVICEUNAVAILABLE

%ERR_COMMERROR

%ERR_PERMISSIONDENIED

%ERR_DISKNOTREADY

%ERR_DISKMEDIAERROR

%ERR_RENAMEACROSSDISKS

%ERR_PATHFILEACCESSERROR

%ERR_PATHNOTFOUND

%ERR_OBJECTERROR

%ERR_GLOBALMEMORYCORRUPT

%ERR_STRINGSPACECORRUPT

  • ASCIIZ string allocations do not proceed past the first occurrence of an embedded $NUL byte in the assigned string.

  • IDISPATCH, IUNKNOWN, and DISPPARAMS are now reserved keywords, to avoid potential conflict with the Windows and PowerBASIC COM subsystems. DISPPARAMS is an intrinsic structure (UDT) that has the following internal definition:

TYPE DispParams

VariantArgs AS VARIANT PTR

NamedDispId AS LONG PTR

CountArgs AS DWORD

CountNamed AS DWORD

END TYPE

  • ERRAPI has been deemed obsolete, and its continued use is not recommended.

  • Error 407 ("Source line too long") compile-time error has been added to the compiler.

  • Error 489 ("Invalid string length") compile-time error now occurs when a string that exceeds 255 characters is assigned to a string equate.

  • Error 503 ("Multiple Main Functions have been defined") compile-time error now occurs if the program contains more than one entry point function.

  • PowerBASIC now supports parentheses around parameters in calls to Subs (and functions that are being called as Subs) when the CALL keyword is omitted. For example, the following lines demonstrate valid syntax:

MyProc (3+z,b)

MyProc ((2+z),b)

Note that if the first parameter is enclosed in parentheses, the whole parameter list must also be enclosed in parentheses. Therefore, the following syntax is not valid:

MyProc (3+z),b

This updated syntax now permits macros to be called using the SUB-style convention if/when the macros expand directly to Function calls. For example:

MACRO sm(Msg) = SendMessage(a, Msg, b, c)

&can now be called like this (when the return value is not required):

sm(x)

  • MACRO prototypes and references may now contain line continuation characters (underscores).

  • #DEBUG PRINT string literals are now excluded from files compiled normally (ie, in non-debug mode).

See Also

New Statements and Functions

Changes to existing Statements and Functions

New in the IDE

Additonal Changes


Running PB/CC from the command line

Running PB/CC

The PowerBASIC Console Compiler is comprised of two core applications: the Integrated Development Environment and the compiler itself. This section describes launching the compiler directly.

See Also

Running PB/CC from Windows

Running PB/CC from DOS

PB/CC Command Line Switches

The Integrated Development Environment


Running PB/CC from Windows

Double-click the PB/CC Compiler icon (PBCC.EXE). A dialog box will appear asking for a file name and compile options:

Type the name of the BASIC source file, plus any desired options, and click the OK button. To abort, click the Cancel button. See below for command-line parameters that may be specified in the dialog box.

See Also

Running PB/CC from DOS

PB/CC Command Line Switches

The Integrated Development Environment


Running PB/CC from DOS

Run PBCC.EXE from a command (DOS) prompt, using a command-line with the following syntax:

PBCC.EXE [/Ipath] [/L] [/Q] [/Cfilename] FileName

…where FileName is the name of the source file to compile. If you just type PBCC (omitting FileName), you'll get a dialog box asking for the name of the file to compile.

PowerBASIC first attempts to open the source file using the FileName specified. If the file cannot be opened and FileName does not have an explicit .BAS extension, PowerBASIC appends .BAS to the specified file name, and attempts to open that file. If FileName is a Long File Name (LFN), it must be enclosed in quotes.

PowerBASIC also supports Long File Names in all metastatements, for example:

#INCLUDE "C:\Program Files\PowerBASIC\LIBRARY.INC"

See Also

Running PB/CC from Windows

PB/CC Command Line Switches

The Integrated Development Environment


PB/CC Command Line Switches

Include /I

The /I command-line option provides the compiler with a search path list when looking for #INCLUDE and #RESOURCE files. Multiple directories can be specified in this path list by separating each path with a semicolon ( ; ).

During compilation, the compiler scans this path list for the necessary files before checking the current (default) directory. To ensure that the current (default) directory is searched ahead of this path list, specify a period followed by a backslash (\) at the beginning of the path list. For example:

/I.\;C:\PBCC\WINAPI;D:\SOURCE

The Include parameter also works with Long File Name (LFN) paths, provided that each LFN is enclosed in quotes. For example:

/I"C:\Program files\My Applications\";C:\PB;"D:\Source Code\"

Log /L

The /L command-line option causes the compiler to generate a log file with all of the compile results, including error code and error line number if an error occurs during compile-time.

Quiet /Q

The /Q command-line option causes the compiler not to display a message box when compiling is finished. This should only be used with the /L option.

Command /C

The /C command-line option specifies a filename that contains the complete command-line. This may be used to specify very long command lines to the compiler of up to 1024 bytes, which may otherwise exceed the operating system limits. This may be useful in situations where the /I path is very long, and the full path to the source file is very long. The /C option may not be used in conjunction with any other command-line options.

See Also

Running PB/CC from Windows

Running PB/CC from DOS

Command Line Dialog Box


Writing Programs in PB/CC

The PowerBASIC Console Compiler for Windows creates EXEcutables (.EXE files) capable of running in the Win32 environment (Windows 95, 98, ME, Windows NT, 2000, and XP). This section discusses the steps and requirements for writing code compatible with PB/CC. It is assumed that you are already familiar with the BASIC programming language (PB/DOS, PB/DLL, PB/Win, QBASIC/QuickBasic, Visual Basic, etc.).

See Also

What is a Console?

Hello, World! Example

Creating Programs

Line numbers and Labels

Long lines

Statement separation

Structured Programming


What is a Console?

A console is a text mode interface connected right to the heart of 32-bit Windows. If you double-click on the MS-DOS icon in Windows, the window that opens with the C:\> prompt is your text console. In it, you can execute commands such as DIR, CLS, COPY, etc. Additionally, you can run text mode applications such as XCOPY32.EXE and PING.EXE. These programs are actually 32-bit Windows applications that use special calls to the Win32 API in order to display text inside the console window.

PB/CC eliminates the need for you to learn these special Win32 API calls in order to create console applications. The whole process is handled for you when you use the PRINT, LOCATE, COLOR, CLS, and other statements in the PB/CC language. This frees you to concentrate on the task at hand.

While not every application you write for Windows will need to be a console application, the console is ideally suited for many types of tasks. For example, using STDIN LINE and STDOUT you can write a utility which takes input from a redirected file, processes it, and sends the output to the standard output device which can be redirected to a new file. A prime example is the SORT.EXE utility that comes with DOS:

SORT < myfile.txt > sorted.txt

The text file "myfile.txt" is read, sorted, and written back to the file "sorted.txt". By using standard input and output for file I/O, the SORT utility can easily be used by other programs which do not have native sorting capability. Porting the SORT utility to a Windows console application gives it the ability to work with much larger files.

DOS programs are limited to available "real mode" memory, which is typically around 640 Kb (less than one Megabyte). 32-bit Windows applications, on the other hand, can access up to two Gigabytes of memory. This is a significant difference.

I.S. and I.T. departments may find PB/CC particularly useful for porting existing DOS applications written in PB/DOS or QuickBasic into the Windows environment. For example, the simple text interface of a DOS application allows data entry operators to quickly enter repetitive information such as telephone orders or inventory data. When ported to a Windows GUI application, the response by the operating system is much slower and often requires additional steps involving a mouse to accomplish the same task. Re-training employees to use the new software and the lower productivity can then raise costs significantly.

Quite often, accounting departments are willing to continue using older DOS programs in order to avoid these problems, even if it means not being able to add additional features such as Internet connectivity, and integration with fast database servers such as MS SQL and Oracle. By porting your application to PB/CC, exactly the same user interface can be retained, and the new features can be added.

Besides the text console interface, PB/CC offers BASIC programmers complete access to the complete Win32 API, including the Winsock API for Internet access, and ODBC for accessing SQL and other database servers. Win32 console applications also have a two Gigabyte flat memory address, so trying to fit everything into 640 Kb or 16 Megabytes of EMS memory is no longer required. A variable can be many Megabytes in size and the limitation of arrays is measured in Gigabytes instead of Kilobytes.

See Also

Writing Programs in PBCC

What is a Console?

Hello, World! Example

Creating Programs


Hello, World! Example

Every programmer is familiar with the "Hello, World!" program. It simply displays the greeting text to an end user. While not a significant program, it does measure two important things. The amount of source it takes to write a complete application and the size of the executable generated by a compiler.

In Visual Basic, you have to create a form with a static text label and write the code to change the label. You then have to set the text of the label. Finally, you have to access several different menu items and property sheets, in order to configure VB to compile a true EXE as small as it possibly can. The result? 1.9 Megabytes of executable and run-time DLLs.

In PB/CC, only three lines of code are necessary:

FUNCTION PBMAIN

PRINT "Hello, World!"

END FUNCTION

And the PB/CC compiler takes care of the rest automatically. No separate steps for linking are necessary. Save the above code as "hello.bas", and at a command prompt (DOS prompt), simply type:

PBCC hello.bas

You're done! You can now run the 11 Kb executable which will greet you enthusiastically.

See Also

Writing Programs in PBCC

Creating Programs

Line numbers and Labels


Creating Programs

Unlike most DOS versions of BASIC, PB/CC requires that all executable code be contained inside a Sub or Function. Only scanned statements (such as metastatements, constant declarations, and procedure declarations) can be placed outside of Subs and Functions. Therefore, every PB/CC program must have either a PBMAIN or a WINMAIN function so Windows can identify where to start executing your program. PBMAIN and WINMAIN are special functions reserved for exactly this purpose.

The following PB/DOS program:

PRINT "At the tone the time will be "; TIME$

BEEP

simply needs to be placed inside a Function called PBMAIN in order to execute:

FUNCTION PBMAIN

PRINT "At the tone the time will be "; TIME$

BEEP

END FUNCTION

When your program is loaded, the function PBMAIN (or WINMAIN) is executed automatically. This is where the main code of your application should be placed. The program stops when there are no more statements in PBMAIN, or an EXIT FUNCTION is encountered within PBMAIN.

If you want to return an error level back to the calling program (or DOS/command prompt) you can assign it as the result to the PBMAIN function:

FUNCTION = 5

or:

PBMAIN = 5

Any value larger than 255 is simply ignored, causing 0 (the default) to be returned.

By default, all variables are LOCAL to Subs and Functions, which means other Subs or Functions cannot see them. If you need to share a variable or array throughout your program, you should use the GLOBAL statement to change the scope. See Variables and Variable Scope for more details.

In DOS BASIC, all variables without a type-specifier (such as % for Integer, ! for Single-precision, etc) default to Single-precision. In PB/CC you must either use a type-specifier with each variable or explicitly declare its type using the DIM, LOCAL, GLOBAL, or THREADED statements:

a% = 1

or:

DIM a AS INTEGER

a = 5

Optionally, you can use a DEFx statement to declare the default variable type. Using DEFSNG would implicitly classify all variables as Single-precision, the same as the default in DOS programs. Explicit declarations take precedence over the implied action of DEFx. For a complete list of differences between PB/CC and DOS BASIC, please see Appendix B.

We recommend that programmers get into the habit of using #DIM ALL or OPTION EXPLICIT in their programs to ensure all variables are declared correctly. While it may take a little extra time, this approach can often prevent subtle bugs from using a variable with the wrong name.

For example, MyVeryLongInteger% could be easily confused with MyVeryLongIntger%, but if an explicit declaration of the variable were made, this type of simple error would not have occurred. This is because the compiler would have flagged the variable with the wrong spelling, rather than accepting it as a separate variable.

While retrofitting #DIM ALL or OPTION EXPLICIT to existing code might be a significant undertaking if the code is large, the effort is often worthwhile.

See Also

Writing Programs in PBCC

What is a Console?

Hello, World! Example

Line numbers and Labels

Long lines

Statement separation

Structured Programming


Line numbers and Labels

Line numbers are Integers in the range 1 to 65535, which serve to identify program lines. PowerBASIC takes a relaxed stance toward line numbers. They can be freely interspersed with labels, and used in some parts of a program and not others. In fact, they do not even need to follow in numeric sequence. No two lines can have the same number, and no line can have both a label and a number. Line numbers are essentially labels.

While line numbers and labels serve the same purpose, their usage is slightly different. Line numbers are really just a concession to compatibility with Interpretive BASIC. Line numbering can lead to bad programming style. Since the numbers themselves can be in any order, they give a false sense of structure to a program. We recommend that you avoid line numbers, and use labels instead.

Labels and line numbers are local to a , so the same line number or label can be used in more than one Sub or Function. However, you cannot use the same name for a label and a global or threaded variable.

Using labels instead of numbers allows you to make the flow of your program much more readable. For example:

GOSUB BuildQuarks

tells you much more than

GOSUB 1723

Each label must appear on a line by itself (though a comment may follow) and it serves to identify the statement immediately following it. Labels must begin with a letter and contain any number of letters, digits, and an underscore. Case is insignificant - THISLABEL, thislabel, and ThisLabel are all treated the same. A colon must follow a label, and statements, which refer to the label, must not include the colon.

PRINT "Now Sorting Invoices"

GOSUB SortInvoices

PRINT "All Done!"

END

SortInvoices: ' This is a legal label

{sorting code goes here}

RETURN

The following is illegal, however:

ExitPoint: a = a + 1 ' a label must be on a line by itself

Finally, it should be noted that symbol names must be unique: a label may not share the name of any other symbol (Sub name, Function name, user-defined type or union definition, variable name, etc), and they are local to the Sub or Function in which they appear.

See Also

Writing Programs in PBCC

Long lines


Long lines

The underscore character ( _ ) can be used to split "logical" lines of source code, across physical lines in the source code file. The underscore character must be preceded by at least one whitespace character.

The effect of using a line continuation character is for "visual" appearance only - the compiler itself treats lines split this way as only one contiguous line of code.

For example, if we take the following line of code:

DECLARE FUNCTION Call32& LIB "CALL32.DLL" ALIAS "Call32" (Param1 AS ANY, BYVAL id&)

We could rewrite this line to place its component parts on separate lines of code for clarity:

DECLARE FUNCTION Call32& _

LIB "CALL32.DLL" _

ALIAS "Call32" _

(Param1 AS ANY, BYVAL id&)

The compiler treats text that appears after the line continuation character as a remark. However, we still recommend that such comments are preceded by a REM or an apostrophe ( ' ) symbol to clearly distinguish remarks from the actual code.

DECLARE FUNCTION Call32& _ ' The prototype declaration

LIB "CALL32.DLL" _ ' The DLL name

ALIAS "Call32" _ ' The exported function name

(Param1 AS ANY, _ ' 1st parameter

BYVAL id&) ' 2nd parameter

See Also

Writing Programs in PBCC

Line numbers and Labels

Statement separation


Statement separation

The colon character (:) can be used to separate multiple statements on a single (logical) line of source code. For example:

FOR x& = 1 TO 10 : INCR y& : NEXT x&

…is directly equivalent to:

FOR x& = 1 TO 10

INCR y&

NEXT x&

In general, placing only one statement per line leads to more readable and maintainable source code; however, using the colon separator can be useful for combining statements on single-line IF/THEN statements, etc. For example

IF x! < 0 THEN INCR y# : INCR z# : DECR Count& : GOTO LastX

See Also

Writing Programs in PBCC

Line numbers and Labels

Long lines

Structured Programming


Structured Programming

For most applications, good programmers use an organized approach to programming called structured programming. The original interpreted BASICs did not really support this kind of programming. However, PowerBASIC, with its control structures and more advanced functions and procedures, is very well suited to structured programming style.

Structured programming is based on the theory that modularization makes for better programs. Modularization means grouping statements together (making modules) that have some relation to each other. In other words, you break up your program into logical functional sections. This makes it easier to write, debug, and understand the program.

Ideally, modules should be no more than a page long. This seemingly arbitrary constraint makes it easier to absorb the entire module at a glance. It is easier to understand a series of ten single-page modules than it is a single ten-page program.

For some projects, after this initial breakup, you're ready to write the program. More complicated problems might require you to break the modules into subsidiary pieces. This process continues until you have refined the material enough so that you can write the code that corresponds to your ideas. This entire process is often described in books as "top-down design", since you start with a general description and work toward a more specific one.

Once you have the logical organization, you can start to design the overall structure of your program. For short, simple programs, these steps may only take a few minutes. For complex programs, it could take months.

To summarize the steps of structured programming (also known as 'top-down programming' or 'top-down design'):

1. Plan your program on paper. Ask yourself the following questions:

a. What is the overall purpose of the program?

b. What kind of input will it need?

c. How will it process that input?

d. What kind of output will the program produce? To where (screen, printer, disk)?

e. How should the input and output look?

f. How can the program be broken up into discrete processes (modules?)

g. How will those modules fit into the main program, and how will they communicate?

h. Can those modules be broken up into even smaller functional segments?

2. Next, write your main program. Don't worry about writing the individual modules that you separated out earlier. Instead, write stubs: Dummy statements that allow the main program to continue. This allows you to test the logic of your main program.

3. Finally (and this step will actually be several steps), write the modules one at a time. Test and debug each module thoroughly before proceeding to the next. If you've broken your module into even smaller processes, write the code for those processes first, test and debug each process, and then put them together to build your module.

See Also

Writing Programs in PBCC

What is a Console?

Hello, World! Example

Creating Programs

Line numbers and Labels

Long lines

Statement separation

Debugging PB/CC Programs


Variables

Variables represent or values. Unlike constants, the value of a variable can change during program execution. Like labels, variable names must begin with a letter and can contain up to 255 letters and digits (although in practical terms you really cannot exceed the length of a line). Be generous in naming important variables. In PowerBASIC, long variable names do not steal run-time memory.

The Single-precision variables, EndOfMonthTotals and emt, both require exactly four bytes of run-time storage. A good rule of thumb is to preserve a balance, keeping variable names short enough so that statements can fit on one line. Many programmers use single-letter variables for counters (i, j, k, l and x, y, z are favorites). However, you can use names like count, total, index, and so on for greater clarity, especially if you have nested loops.

PowerBASIC has many built-in variable types: Dynamic string; Fixed-length string; ASCIIZ string; Field, Integer; Long integer; Quad integer; Byte, Word; Double word; Single; Double; and Extended floating point; Currency and CurrencyX; Variant, Object, Guid, plus Pointer, arrays, and Bit and Sbit bitfield subtypes.

Declaring a variable as a specific type:

Use the DIM statement to declare a variable and use the AS type syntax:

DIM iVar AS INTEGER

Appending a type-specifier to the variable name:

bat# = 1.312 ' bat# is a Double-precision variable

hat% = 3 ' hat% is an Integer variable

DEFINT c ' Variables beginning with c are now Integer

cats = 16 ' cats is an Integer variable by DEFINT

Bear in mind that cat?, cat%, cat&, cat&&, cat!, cat#, cat##, cat@, cat@@, and cat$ are ten separate variables. Although using cat over and over again to create different variables like this is legal, good programming practice suggests that you use somewhat different names for different variables. It is also much better to use descriptive and more easily understood names for your variables rather than single letters. It's extremely difficult to debug a program in which x@ has been entered instead of x! or x#. Imagine the confusion of trying to distinguish x&& and x&. If you had used variable names like count!, result#, remain##, and company$, you would have had considerably less trouble keeping your variables (and their types) apart.

See Also

Default Variable Typing

Variable Scope

THREADED variables

LOCAL, GLOBAL and STATIC considerations


The PB/CC Integrated Development Environment

This topic will help you learn how to use all the options available in the PowerBASIC Integrated Development Environment (which we will refer to as the IDE). You will learn how to use the editor, move from window to window, menu to menu, and choose menu commands. The Integrated Debugger is discussed in an earlier chapter.

To launch the IDE, double-click the CCEDIT.EXE icon, type CCEDIT at the command-line, or use the START menu entry.

The PB/CC editor (CCEDIT.EXE) can also be launched from the command-line, and supports the following command-line options:

CCEDIT.EXE [/G:row,col:] [/P:MainFile] [Filename]

The command-line options may be prefixed with either a forward-slash (/) or a hyphen (-). Multiple files can be specified for the Filename parameter, each separated by space characters. Long file names should be enclosed in double-quote marks (").

Goto /G:

The /G: command-line option causes the IDE to move the caret to the row and column specified. For example, /G:10,20: causes the caret to start at line 10, column 20. The /G option must be terminated by a trailing colon.

CCEDIT.EXE /G:10,20: "Project Bluepad.bas"

Primary Source File /P:

The /P: command-line option specifies the name of the file that will be set as the Primary Source File. This option is useful when working on large applications that span multiple source code files, especially when loading multiple files at startup. When a compile/execute/debug operation begins, the IDE automatically uses the Primary Source File as the "main" file, regardless of which other files are loaded or have focus in the IDE.

The Primary Source File will be one of the files loaded into the IDE.

CCEDIT.EXE /P:Project.bas "Support Library.inc" Project.rc "Data file index.txt"

See Also

The PowerBASIC User Interface

Toolbar Buttons

Editor Hot Keys

IDE Context Menu

Custom Help Files

File Templates

Code Finder Dialog Box

Command Line Dialog Box

Debugger Evaluate Dialog Box

Find Dialog Box

Go to Line Dialog Box

Primary Source File Dialog Box

Replace Dialog Box

IDE Options


The PowerBASIC User Interface

Insert File | Insert a document (file) at the caret position in the current document.
New File | Create a new empty document in the editor.
New File As… | Create a new empty document in the editor, using a specified file template.
Open File… | Use the Open dialog box to load an existing document.
Save File | Save the current document using its current name.
Save File As… | Save the current document using a new name.
Print… | Print the current document.
Close File | Close the current document.
Close All Files | Closes all files open in the editor.
[Recent Files list] | A list of the most recently loaded source code documents.
Command Prompt | Open a command prompt window ("DOS box").
Exit | Close all open documents and exit the IDE.

Undo | Undo the last action or deletion.
Clear | Delete the selected text.
Cut | Remove selected text and place it in the Clipboard.
Copy | Copy the selected text and place it in the Clipboard.
Paste | Copy text from the Clipboard into the current document.
Select All | Select all the text in the current document.
Block selection… | Perform the selected operation on every line in the selected block. Operations include comment/uncomment, indent/unindent by a tab level, and indent/unindent by one space.
Find | Search the current document for a selected word or phrase. See Find dialog for more information.
Find Next | Search the current document for the next occurrence of the previous word or phrase used in Find.
Replace | Search the current document for a selected word or phrase and replace it. See Replace dialog for more information.
Go to Line | Move the caret to the selected line number in the current document. See Go to Line dialog for more information.
Go to Bookmark | Move the caret to the selected bookmark in an open document.
Code Finder | The Code Finder dialog presents a list of Subs, Functions, and Macros in current document, to quickly jump to a selected section of code.

Compile | Compile the current source document or, if you’ve chosen one, the Primary Source File.
Compile and Execute | Compile and then Run the current source document or, if you’ve chosen one, the Primary Source File.
Compile and Debug | Compile and then Debug the current source document or, if you’ve chosen one, the Primary Source File.
Set Primary Source File | Define which module to compile when the Compile or Debug options are selected. The Primary Source File can also be specified via the /P: IDE command-line switch.
Command Line | Set the command-line to pass to the program when debugging or executing from within the IDE. See COMMAND$.

PowerBASIC COM Browser

Open the PowerBASIC COM Browser (PBROW.EXE) to generate COM interface code from Object libraries.

Cascade | Stack all open windows and overlap them so that part of each underlying window is visible.
Tile Horizontal | Arrange your open windows from top to bottom without overlapping one another.
Tile Vertical | Arrange your open windows from left to right so that they display next to each other.
Options | Display the Options dialog, for configuring the PowerBASIC IDE.
[current files list] | A list of files currently opened in the PowerBASIC IDE.

Run | In edit mode, this will compile and then debug the current source document or, if you’ve chosen one, the Primary Source File. In debug mode, this causes your program to run until it hits a breakpoint or ends.
Run to Caret | Begin running the program. It continues to run until the debugger either reaches the current line, or encounters a breakpoint, etc. CTRL+F8 is the hot-key for the Run to Caret option.
Animate | The debugger runs the program using an automated Step-Into technique. Execution continues until a breakpoint is reached, the Stop button is pressed, or the program completes. The Animate delay can be set through the IDE's Options Dialog.
Stop | Halt the debugger. If the debugger is already halted, this has no effect. CTRL+BREAK is the Stop hot-key.
Step Into | If the current line contains a call to a Sub or Function, the debugger traces execution into that Sub or Function. You cannot step into an API call, or into an external module. F8 is the Step Into hot-key.
Step Over | The debugger executes the current line of code. If the line contains a reference to a Sub or a Function, the debugger executes that code without tracing into the Sub or Function. SHIFT+F8 is the Step Over hot-key.
Step Out | The debugger runs the code until the current Sub or Function exits. If the current function is PBMAIN or WINMAIN, the code is executed until the program is finished or another breakpoint is encountered. CTRL+SHIFT+F8 is the Step Out hot-key.
Evaluate Variable | Evaluate or modify a variable, or add/remove a variable in the Watch window. It is not possible to use this to change the length of a string. Also see Watch CPU Registers.
Clear all Watches | Remove all variables from the Watch window.
Toggle Breakpoint | Set or release a breakpoint on the current line. F9 is the Toggle Breakpoint hot-key.
Clear all Breakpoints | Release all breakpoints in the program.
Console on Top | Set or release the permanent "Always on Top" or "topmost" property of the console window created by the debugger. When the console is not permanently topmost, it can be temporarily brought into the foreground (above the debuggers windows) by the Console button on the toolbar.
Watch CPU Registers | Show or hide the Register Watcher window, which lets you see the state of the CPU registers and flags when debugging.
Variable watch window | Show or hide the Variable Watcher window, which lets you see the state of the ERR function and any variables you choose to watch when debugging.
Program Restart | If the current program is halted/stopped, the program will be reset, ready for debugging to commence again. SHIFT+F5 is the Reset hot-key.
Exit Debugging | Halts the current program and terminates the debugger. The variable list in the Watch window is retained between debugging sessions, until the IDE is closed.

Dynamic Help | Displays the help file with information about the closest word to the edit caret.
PB/CC Contents | Displays the PB/CC help files table of contents tab.
PB/CC Index | Displays the PB/CC help files index tab.
PB/CC Search | Displays the PB/CC help files search tab.
Help for Windows SDK | Display the Win32.hlp help file (if installed).
[other help files] | Display an add-on help file.
PowerBASIC Web Site | Launch your default Web Browser and connect to the PowerBASIC web site.
About CCEdit | Display version information for the PowerBASIC IDE.

The PowerBASIC User Interface

The PowerBASIC IDE was designed to provide you with the tools you need to quickly and intuitively develop high-performance applications. This section briefly describes each element of the IDE.

  • File Menu

  • Edit Menu

  • Run Menu

  • Tools Menu

  • Window Menu

  • Debug Menu

  • Help Menu


Toolbar Buttons

 | Create a new empty document (file) in the editor.
 | Use the Open File dialog box to load an existing document.
 | Save the current document if it has been modified and unsaved.
 | Print the current document to a printer.
 | Move the selected text from the document to the clipboard.
 | Copy the selected text from the document to the clipboard.
 | Copy the text from the clipboard into the current document.
 | Search the current document for a word or phrase. See Find dialog for more information.
 | Search the current document for a word or phrase and replace it. See Replace dialog for more information.
 | Compile the current source document (or Primary Source File if specified).
. | Compile and Execute the current (or Primary) source document
 | Compile and Debug the current (or Primary) source document.
 | Launch the Go to Line dialog to jump to a specific line in the current document.
 | Launch the Code Finder dialog, which presents a list of Subs, Functions, and Macros in current document, to quickly jump to a selected section of code.
 | Display the PowerBASIC or the WIN32.HLP file.


Editor Hot Keys

The following table summarizes the hot-keys available in the PowerBASIC IDE Editor Window:

Keystroke | Description
F1 | Help
CTRL+ALT+F1 | Display the Table of Contents tab of the PowerBASIC Console Compiler Help File
CTRL+ALT+F2 | Display the Index tab of the PowerBASIC Console Compiler Help File
CTRL+ALT+F3 | Display the Search tab of PowerBASIC For Windows Help File
F2 | Code Finder dialog
F3 | Find dialog/Find next
SHIFT+F3 | Find previous
F4 | Duplicate current line
CTRL+F4 | Close current document
ALT+F4 | Exit PBEDIT
F5 | Compile and debug (if in edit mode) or Run program (if in debug mode)
CTRL+F5 | Animate program.
F6 | Clear to end-of-line
CTRL+F6 | Switch to the next document window
F8 | Step into next program line (when debugging)
CTRL+SHIFT+F8 | Step out of current procedure (when debugging)
SHIFT+F8 | Step over next program line (when debugging)
CTRL+F8 | Run to caret (when debugging)
F9 | Break (stop the program being debugged)
ALT+BACKSPACE | Undo
SHIFT+INSERT | Paste text from clipboard
SHIFT+DELETE | Cut text to clipboard
CTRL+DELETE | Cut text to clipboard
CTRL+INSERT | Copy text to clipboard
CTRL+HOME | Move to start of document
CTRL+END | Move to end of document
CTRL+PAGEUP | Move to start of document, maintaining caret position on screen
CTRL+PAGEDOWN | Move to end of document, maintaining caret position on screen
CTRL+0 | Go to bookmark 0
ALT+0 | Set bookmark 0
CTRL+1 | Go to bookmark 1
ALT+1 | Set bookmark 1
CTRL+2 | Go to bookmark 2
ALT+2 | Set bookmark 2
CTRL+3 | Go to bookmark 3
ALT+3 | Set bookmark 3
CTRL+4 | Go to bookmark 4
ALT+4 | Set bookmark 4
CTRL+5 | Go to bookmark 5
ALT+5 | Set bookmark 5
CTRL+6 | Go to bookmark 6
ALT+6 | Set bookmark 6
CTRL+7 | Go to bookmark 7
ALT+7 | Set bookmark 7
CTRL+8 | Go to bookmark 8
ALT+8 | Set bookmark 8
CTRL+9 | Go to bookmark 9
ALT+9 | Set bookmark 9
TAB | Indent marked block by one tab level
SHIFT+TAB | Outdent marked block by one tab level
SPACE | Indent marked block by one space
SHIFT+SPACE | Outdent marked block by one space
CTRL+A | Select all
CTRL+B | Go to Bookmark dialog
CTRL+C | Copy text to clipboard
CTRL+D | Duplicate current line
CTRL+E | Build and Execute
CTRL+F | Find dialog
CTRL+G | Go to Line dialog
CTRL+I | Toggle auto-indent mode
CTRL+K | Clear to end-of-line
CTRL+L | Select current line
CTRL+M | Compile the current document (or primary source file, if any)
CTRL+N | Create a new document, using the default file template
CTRL+O | Open an existing document
CTRL+P | Print the current source document
CTRL+Q | Comment-out marked block
CTRL+SHIFT+Q | Uncomment-out marked block
CTRL+R | Find and Replace dialog
CTRL+S | Save the current document
CTRL+T | Delete the word at the caret
CTRL+U | Paste text from clipboard
CTRL+V | Paste text from clipboard
CTRL+X | Cut text to clipboard
CTRL+Y | Cut current line to clipboard
CTRL+Z | Undo last change


IDE Context Menu

When editing a file in the PowerBASIC IDE, a popup context menu is available by right-clicking the mouse within the edit window. The available content of the menu is automatically determined by position and text located at the point where the context menu is activated. The full context menu looks like this:

Undo | Reverse the most recent edit change from the undo stack. Each successive Undo operation removes the most recent change from the undo stack and reverses the editing in the document. Undo operations can be performed until the undo stack is depleted; however, the undo stack is cleared when a Save or Compile operation is performed.
Clear | Delete the currently selected block of text.
Cut | Copy the currently selected block of text to the clipboard, and delete the highlighted block from the file.
Copy | Copy the currently selected block of text to the clipboard.
Paste | Paste the contents of the clipboard into the current file.
Select Line | Select (highlight) the complete line at the context-menu point.
Select Block | Select (highlight) a complete block of code. This menu item is available when the context menu is activated on the first line of a formal block. Formal blocks include those that begin with the #PBFORMS metastatement (PB/Win only), the FOR/NEXT and SELECT CASE blocks, plus the usual CALLBACK, FUNCTION, SUB, TYPE, and UNION statements.
Select All | Select (highlight) the entire contents of the current file.
Run to Caret | Run the program until execution reaches the current caret position (debug mode only).
Watch Variable | Add the variable at the current caret position to the Variable Watcher window (or remove it, if it’s already there). The Variable Watcher window is visible only in debug mode.
Evaluate Variable | Evaluate or modify the variable at the current caret position (debug mode only).
Toggle Bookmark | Add or remove a bookmark at the current caret position.
Toggle Breakpoint | Add or remove a breakpoint at the current caret position. Breakpoints only work in debug mode.
Help | Launch context-sensitive help. If the context menu point is on a PowerBASIC keyword, the appropriate topic is displayed in the PowerBASIC help file. If the context point is not a recognized keyword, the WIN32.HLP file is launched instead. This feature is useful for context-sensitive help on both reserved keywords, and API functions and data structures, etc.
Open File | Open the file indicated in the #INCLUDE metastatement at the context menu point. The item is only enabled when the context menu point is targeting an #INCLUDE metastatement. Open File works even if the #INCLUDE metastatement is commented out.

See Also

The Integrated Development Environment

Debugging PB/CC Programs


File Templates

A file template is the framework for a new file, which you can load into the IDE with the "New File As…" option. While a template can contain anything you like, it is typically used to automate the basic boilerplate needed for a new document. For example, the "Generic PB program" template creates a new file with the following information already filled out:

#COMPILE EXE

#DIM ALL

FUNCTION PBMAIN () AS LONG

END FUNCTION

What’s more, the caret is conveniently placed in the middle of the FUNCTION block for you, letting you get right to programming!

You can readily build templates of your own, or modify the ones that come with the IDE. A template is simply a text file created according to a few simple rules. Let’s look at the default template (you can load it into the IDE, NotePad, or any other text editor). PowerBASIC templates use ".PBTPL" for their file extension. The default template is "Default.pbtpl", then. You can find it in the Bin subdirectory for your compiler ("C:\PBCC\Bin", by default).

The first line starts out with a number:

1

This is the template version number, 1 (one). There may be other options in the future, but the number must always be 1, for now.

The second line contains the file extension to apply to files that are created with this template:

.bas

The third line gives the name of the template, which will be used in the "New File As…" menu:

Generic PB program

The following lines give the text to be filled into the file created by the template. There is one special character, the "|" vertical bar or pipe symbol. This indicates where the caret should be placed after the text is filled in.

#COMPILE EXE

#DIM ALL

FUNCTION PBMAIN () AS LONG

|

END FUNCTION

That’s all there is to it!

After creating a new template, save the .PBTPL file in the Bin subdirectory for your compiler. The default location for this is, typically, "C:\PBCC\Bin\". Now, the next time you start the PowerBASIC IDE, your custom template will be available on the "New File As…" menu.

See Also

The Integrated Development Environment


Custom Help Files

The PowerBASIC IDE has built-in context-sensitive help for PowerBASIC keywords. If the caret is placed on a keyword when you invoke help, you will get help for that specific keyword. Now, you can add context-sensitive help for your own help files. Here’s how.

For each help file, create a text file with a name of your choice, with a file extension of .PBKeys (using the PowerBASIC IDE, NotePad, or any other text editor). The first line of the text file must contain the name of the help file, as it will be shown in the IDE’s help menu, like so:

MenuName="PowerTree 1.1"

The next line of the PBKeys file specifies the name and location of the help file. If the help file is in the same directory as the .PBKeys file, you can specify just the filename, without the path. Otherwise, you must provide a fully-qualified absolute path:

HelpFile="C:\PTreeW11\PwrTree.hlp"

Each following line specifies a help keyword. This keyword must be present in the index of the help file, in order for context-sensitive help to work.

HelpKey="AccessBlock"

HelpKey="ptCreateIndex"

HelpKey="ptAdd"

…and so forth.

When you’re done, save the .PBKeys file in the Bin subdirectory for your compiler. The default location for this is, typically, "C:\PBCC40\Bin\". Now, the next time you start the PowerBASIC IDE, your custom keywords will be recognized by the context-sensitive help system. You will also be able to load the help file from the Help menu.

If your help file does not appear in the Help menu when you start the IDE, make sure the HelpFile line of the .PBKeys file specifies the correct location and name for your help file.

The complete PowerTree .PBKeys file, "PowerTree 1.1.PBKeys", is already installed in your compiler’s Bin subdirectory. Please note that the custom help list is only loaded if you have PowerTree 1.1, and it’s installed at the location specified in the HelpFile line of the PBKeys file.

See Also

The Integrated Development Environment


IDE Code Finder Dialog Box

The Code Finder Dialog works from within the editor and debugger, presenting a list of Subs and Functions in the current document (file). The list is presented in a grid-like style, and the list can be sorted. To jump to a Sub or Function shown in the list, double click the item, or select the item with the arrow keys, and press the ENTER key.

Columns in the Code Finder list can be sorted simply by clicking on the appropriate Column Header. The first click on Header sorts the list in ascending order. Each subsequent click reverses the sort order for that column.

List | The list of Subs and Functions available in the current document (file).
OK | The editor or debugger jumps to the item selected in the list, if any.
Cancel | The dialog is dismissed without any change in position in the document (file).

See Also

The Integrated Development Environment

Debugging PB/CC Programs


IDE Command Line Dialog Box

Command Line Dialog Box

The Command Line Dialog allows the programmer to specify an arbitrary command-line parameter string that is passed to the application when the Compile and Execute, or Compile and Debug options are used.

The result can be read with COMMAND$ within the program, for the purposes of testing the application.

Arguments | An arbitrary string passed to the application in the COMMAND$ parameter.
OK | The text in the Arguments field is accepted and retained for the session.
Cancel | The previous command-line text, if any, is retained unaltered.

See Also

The Integrated Development Environment


Debugger Evaluate Dialog Box

The Evaluate Dialog provides a central position to examine and modify variables and add or remove variables from the Watch Window.

Variable Name

The name of the variable to be evaluated or watched. Array elements and indexed can be evaluated/watched provided the subscript index is a numeric literal or a single (scalar) variable, for example A$(10) or @B[x&].

Modify

If the Value field is explicitly modified, the Modify button commits the change back to the variable’s own storage. This provides a way of adjusting variables in a program running in the debugger, for example, so specific tests can be applied to a program. Note that Modify cannot change the length of a .

Watch on/off

The variable in the Variable Name field is added or removed from the Watch Window as appropriate.

Close

The Evaluate Dialog is closed, and control returns to the main debugger window.

See Also

Debugging PB/CC Programs

The Integrated Development Environment


IDE Find Dialog Box

Find Dialog Box

The Find Dialog Box allows you to search the currently displayed source code file for a specific phrase or word. You can limit the number of matches by specifying options such as Match Whole Words or Match Case.

Find What | Enter the phrase or word to search for. For example, searching for PRINT will locate every instance of that word in the current file. The text should be entered as it is anticipated to be formatted in the current file. For example, the number of spaces between words must match the number specified in the Find What field. Do not include quotes unless the anticipated match also includes quotes.
Match Whole | This excludes matches that occur within a word. For example, with Match Whole Words enabled, searching for LOG will not match on DIALOG, but will match on LOG(x).
Match Case | When Match Case is enabled, the Find What text must exactly match the capitalization of the word or phrase in the current file. For example, searching for Print will match Print, but not PRINT or print.
Direction | By default, searching starts at the current caret position, and moves toward the end of the current file. The Direction options allow you to specify whether the search should proceed from the caret position upward toward the top of the file, instead of downward.
Find Next | Instructs the editor to locate the next match in the current file. If no further matches are located, a notification appears. If a match is made, the matching text is highlighted in the file.
Close | Cancel the Find Dialog. After the Find Dialog has been closed, you can repeat the last Find operation by pressing the F3 key, even if you have opened or switched to a new file. However, the Find What text is not preserved between sessions of the IDE.
Help | This help topic.

See Also

The Integrated Development Environment


IDE Go to Line Dialog Box

The Go to Line Dialog allows you to jump to a specific line of code in the current editor file or debugger file (depending on the context when the Go to Line Dialog was launched).

Go to Line can be very useful when working in the debugger, and a change to the source code can be made while the debugger is still running.

Last Line | The last "physical" line of code in the current document/file.
Current Line | The line number where the Caret is currently located.
Go to Line | Enter the line number where you wish to jump to. This must be in the range 1 through Last Line, inclusive. Values outside of this range are ignored, but the Go to Line Dialog is not dismissed.
OK | If valid, the IDE jumps to the line number indicated in the Go to Line field, and the Go to Line Dialog is dismissed.
Cancel | The Go to Line Dialog is canceled, and the Caret position remains unaltered.

See Also

The Integrated Development Environment

Debugging PB/CC Programs


IDE Primary Source File Dialog Box

Primary Source File Dialog Box

The Primary Source File Dialog allows the programmer to define which source code module is regarded as the "main" program file. That is, when a compile/execute/debug operation begins, the IDE automatically uses the Primary Source File as the "main" file, regardless of which other files are loaded or have focus in the IDE.

The Primary Source File will be one of the files loaded into the IDE, and this can be via the Recent Files list (if the Reload previous file set at start IDE option is enabled). The Primary Source File can also be specified with the /P: IDE command-line switch.

Primary Source File | The name of the file designated to be the main file to compile and/or debug, even when multiple files are open. Choose None to disable the Primary Source File usage.
OK | The name in the Primary Source File list box is accepted and retained for the session as the "main" source code file.
Cancel | The previous Primary Source File, if any, remains unaltered.

See Also

The Integrated Development Environment


IDE Replace Dialog Box

The Replace Dialog provides a mechanism similar to the Find dialog, but allows you to selectively replace matched text with new text, or leave the text unchanged. Replace works from the current caret position to the end of the current file. Replace All works on the complete file.

Find What | Enter the phrase or word to search for. For example, searching for PRINT will locate every instance of that word in the current file. The text should be entered as it is anticipated to be formatted in the current file. For example, the number of spaces between words must match the number specified in the Find What field. Do not include quotes unless the anticipated match also includes quotes.
Replace With | Enter the text to replace the matched text with. The replacement text should be entered exactly as you wish to appear in the file. Replace With may be left empty (blank), causing matched text to be deleted from the current file when Replace or Replace All is selected.
Match Whole | This excludes matches that occur within a word. For example, with Match Whole Words enabled, searching for LOG will not match on DIALOG, but will match on LOG(x).
Match Case | When Match Case is enabled, the Find What text must exactly match the capitalization of the word or phrase in the current file. For example, searching for Print will match Print, but not PRINT or print.
Find Next | Instructs the editor to locate the next match in the current file. If no further matches are located, a notification appears. If a match is made, the matching text is highlighted in the file. Once a match is made, you can replace the text with the Replace button.
Replace | Replace the highlighted text with the text in the Replace With field.
Replace All | Replace every match in the current file, without prompting for each match. The Replace All operation starts at the beginning of the current file, and proceeds to the bottom of the file, without any prompting on matches, and replaces text with every match made.
Close | Cancel the Find Dialog. After the Find Dialog has been closed, you can repeat the last Find operation by pressing the F3 key, even if you have opened or switched to a new file. However, the Find What or Replace With text is not preserved between sessions of the IDE.
Help | This help topic.

See Also

The Integrated Development Environment


IDE Options Dialog Box

IDE Options

The section describes the options that are available to customize the IDE environment, file paths, and compiler behavior. These options are divided into seven categories:

Browsing for Include folders | Settings for Compiler search order of include files
Compiler Preferences | Settings for file types, the Compiler, and the Debugger
Editor Preferences | Main settings for the Editor
Editor Text Preferences | Font settings for the Editor
File Preferences | File handling settings for the Editor
General Preferences | General configuration settings and options
Printer Preferences | Font settings for printing source code
Syntax Color Preferences | Syntax color settings for editing and printing

See Also

The Integrated Development Environment

Debugging PB/CC Programs


Browsing for Include folders

The Include Paths Selection dialog provides a simple method of creating an Include file list for the PowerBASIC compiler, and the Resource Compiler. The Include folder list specifies the search order that the compilers use to locate #INCLUDE and #include files. The Include Paths Selection Dialog box is launched by the Ellipsis buttons on the Compiler Preferences tab page.

Folder list

The list of folders in a drag list control. The folders appear in the order in which the compiler search for #INCLUDE (PowerBASIC) or #include (Resource Compiler) files. There are two ways to rearrange the order of folders:

1. Click and drag the individual folder names up and down in the Folders List; or…

2. Select (highlight) a folder and use the Move Up and Move Down buttons to reposition the folder in the list.

Add Folder

Launch the standard Windows "Browse for Folder" dialog, where the folder tree can be navigated. The default folder is the currently selected folder in the Folders list to the left of the Add Folder button or the current folder if none are selected. The Browse for Folder dialog looks like this:

Delete

Delete the currently selected folder. If all folders are deleted, a new entry specifying the current folder is automatically created, ensuring at least one folder appears in the list.

Move Up

Move the currently selected folder up one position in the Folders List, increasing the search priority of the selected folder. The compilers search the Folders List in the order they appear.

Move Down

Move the currently selected folder up down position in the Folders List, decreasing the search priority of the selected folder. The compilers search the Folders List in the order they appear.

OK

Accept all changes to the Folders List, and return to the Compiler Preferences dialog.

Cancel

Cancel any changes made to the Folder List, and return to the Compiler Preferences dialog.


Compiler Preferences

Include File Paths

PB Include

The path (or paths) where the Compiler may search for source code files referenced in #INCLUDE metastatements, and PBR files referenced with #RESOURCE metastatements. Multiple paths are automatically separated with semi-colons. Use the Ellipsis button (…) to adjust the Include path settings - see Browsing for Include folders for more information. Note that this field behaves identically to the /I command-line compiler parameter.

RC Include

The path where the Resource Compiler may search for additional resource files, as may be referenced within a RC file. The text entered into this field is prefixed with "/I" and passed as a command-line parameter to the nominated resource compiler. Use the Ellipsis button (…) to adjust the RC Include path settings - see Browsing for Include folders for more information.

Compiler Options

Beep on completion

The default system sound is played when compilation is completed successfully. The default system sound can be changed in Control Panel.

Display results

After compilation of PowerBASIC source code, a dialog can be displayed to summarize the compilation results, providing details on compiled code size, data and string literal size, etc. This dialog must be dismissed before the compiled application can be executed or debugged. If a compile-time error occurs, this results dialog is always displayed by PowerBASIC.

Create log file

During compilation, a log file is created in the same directory as the primary source file. The log file contains the same information as the Display Results dialog discussed above. The file is assigned the same "base name" as the main source code file, but with the extension .LOG (i.e., PROJECT1.LOG). In case of a compile-time error, this log file will contain details of the nature of the error (in addition to the compile-time error message display produced by the compiler itself).

Debugger Options

Break on Error

Causes the debugger to stop after every statement to check the error status and then automatically halt program execution when an error occurs (non-zero ERR value). In debugging with this setting enabled, programmed with larger iteration counts can reduce debugging speed to unacceptable levels. For best results, it is instead recommend to enable #DEBUG ERROR ON in your program instead of this option.

Animate Delay

The debugger's Animate debug mode pauses for at least the given amount of time before execution of the next line of code occurs. Animation is very useful for watching the general flow of a program. The delay is specified in milliseconds (mSec). The larger the delay value, the greater the delay between execution of lines of code. The default value is set for 333 milliseconds (1/3 of a second).


Editor Preferences

PB Source | This is the file extension, or list of extensions, you expect to use for main PowerBASIC source code modules: programs you can compile directly. You may enter multiple extensions by separating each with the vertical bar or "pipe" character, "|". The default setting for PB Source is ".bas".
PB Include | This is the file extension, or list of extensions, you expect to use for PowerBASIC include files: bits of code that you will #include in a main module before compiling. You may enter multiple extensions by separating each with the vertical bar or "pipe" character, "|". The default setting for PB Include is ".bi|.inc".
RC Source | This is the file extension, or list of extensions, you expect to use for resource scripts: programs that are compiled with the RC.EXE resource compiler. You may enter multiple extensions by separating each with the vertical bar or "pipe" character, "|". The default setting for RC Source is ".dlg|.rc".
RC Include | This is the file extension, or list of extensions, you expect to use for your PowerBASIC include files: bits of code that you will #include in a resource script before compiling with the RC.EXE resource compiler. You may enter multiple extensions by separating each with the vertical bar or "pipe" character, "|". The default setting for RC Include is ".h".
Register file extensions with Windows | Check this box to register your selected file extensions with Windows. This allows Windows to automatically load files with these extensions into the PowerBASIC IDE when you click on a file in Explorer, or launch it from the Start menu, for example.
Keep Line Length | Clicking the mouse cursor beyond the right-most character of a line does not extend the line beyond the end of the actual text content.
Wrap Caret at Line Ends | Check this box to have left-arrow wrap to the previous line, and right arrow wrap to the next line, instead of stopping at the start or end of the current line.
Auto Indent | The IDE provides automatic indenting when ENTER is pressed, in order to assist with writing visually structured code. The Indent depth depends on the context of the text on the preceding line. For example, if the previous line starts with the word FUNCTION, the following line is automatically indented. Auto-indent can be toggled from within the editor with the CTRL+I hot-key combination. See Tab Size.
Tab Size | The number of characters between "tab stops", in the range 1 through 8 inclusive. When the TAB key is pressed, the IDE substitutes space characters to move the caret to the next tab stop position. Tab Size also affects the Auto Indent depth.
Keyword Case | The IDE automatically sets the capitalization of reserved keywords as directed by this option. The use of capitalization can help readability of code. By default, the IDE applies keyword capitalization to BASIC source code files only, which are determined by the file extensions set under Compiler Preferences. Use care when applying capitalization to resource files (for example, .RC files, .H, and .DLG files) as these usually contain case-sensitive keywords. Custom keyword colors can be configured in the Syntax Color Preferences page, and the editor font can be configured on the Editor Text Preferences page.


Editor Text Preferences

Font | The IDE editor uses a fixed-width ANSI font. By default, the IDE uses the Courier New font. However, an alternative fixed-width font may be selected from this drop-down list.
Size | The desired point size of the IDE editor font. The default is usually 12 point.
Sample Text | How the editor text will appear with the selected font and at the selected font size.


File Preferences

Autosave editor files | If Autosave is "on", the IDE will save all open files before compiling or debugging code. Autosave also saves files when switching to PowerBASIC Forms™ via the F7 hot-key, or via the Tools menu (PB/Win only). If Autosave is "off", only the primary source file will be saved before compiling or debugging.
Back up editor Files | When saving files, the IDE will rename the previous disk file with the .BAK file extension, and save the latest copy under the original filename. This option provides a simple method of preserving the previously saved version of the source code.
Save using tab compression | When saving files, the IDE can compress leading spaces on every line into tabs, using the tab size specified under Editor Preferences. This helps maintain your preferred indentation levels when working with others who choose different tab sizes. It also reduces your source file size.
Reload previous file set at start | The files that were previously open in the IDE are reloaded automatically the next time the IDE starts up. Files are reloaded in their original order, regardless of the Primary Source File, if any. The caret position(s) are maintained. The Primary Source File can be specified via the /P: IDE command-line switch, and the size of the Recent Files list can be adjusted with the Maximum Recent Files option below.
Clear missing recent files at start | The IDE checks the Recent Files list (located in the File menu) at start up. If any file cannot be located and read, the corresponding entry in the Recent Files list is automatically removed. Where files are located across a network or removable media, this option may need to be unchecked.
Maximum Recent files | Specifies the maximum number of Recent Files tracked in the File menu, in the range of 0 through 9. Select 0 to disable the Recent Files list; otherwise, the selected number of previous files is tracked between sessions. Also see Reload previous file set at start.
Start in default folder | The IDE's initial directory for File Open/Save, etc. This option will retain the default directory assigned to the IDE at start-up. The folder option allows the initial directory to be specified in the "Start In" settings of a desktop Shortcut, etc.
Start in last folder | The IDE uses the directory that was in use the last time the IDE was closed down. This option may useful when working for extended periods on the same project.


General Preferences

General Preferences

Ask before exiting

When selected, a confirmation dialog will appear when the IDE is about to be closed. Canceling the dialog will prevent the IDE from closing. The IDE will always prompt to save any files that have not been saved since their last modification, regardless of whether this option is selected.

Editor output to messagebox

When selected, editor output (such as error codes and compilation status) is displayed using message boxes as well as the output window.

Debugger output to messagebox

When selected, debugger output (such as errors and #DEBUG PRINT information) is displayed using message boxes as well as the output window.

Toolbar Button size

Small Buttons

The IDE and debugger toolbars are displayed with small buttons and icons, allowing the maximum amount of screen real estate for the editor windows. If changed, this option comes into effect when the IDE is next launched.

Large Buttons

Complement of Small Buttons. Larger toolbar buttons are easier to "hit" with the mouse, but slightly reduce the amount of space available for editor windows.

WIN32.HLP File Location

WIN32.HLP File Location

The full drive, path, and filename of the WIN32.HLP file can be manually entered here, or it can be located via a standard Open File dialog using the Ellipses button to the right of the edit control. The WIN32.HLP file contains detailed information on a large number of API calls. Once configured, the PowerBASIC IDE can provide context-sensitive help on many common API functions. The WIN32.HLP file can be downloaded from the PowerBASIC web site from http://www.powerbasic.com/support/downloads/files/win32.zip (approximately an 8 MB download).

Size and Position

Main IDE Window

When the IDE is launched, the initial window size is determined by this setting (Normal, Maximize, or As Last Session).

New File Window Size

When a source code file is opened, or created from scratch, the initial size of the editor window is determined by this setting (Use MDI Defaults, Always Maximize, Never Maximize),


Printer Preferences

Font | As with the IDE edit window, printing is performed with a fixed-width ANSI font. By default, this is the Courier New font. However, an alternative font may be selected from this drop-down list.
Size | The desired point size of the printed font.
Sample Text | How the printed text will appear with the selected font and at the selected font size.


Syntax Color Preferences

Use Syntax Color in Editor | The IDE can show colored reserved keywords and other types of syntax in the source code file. Both the text (foreground) and background colors can be individually customized for each syntax type. The use of highlighting can increase readability of code. Also see Use Syntax Color when Printing.
Use Syntax Color when Printing | The IDE can optionally print source code with coloring applied to the reserved keywords and other syntax types. Printing with syntax coloring enabled only affects the text (foreground) - background coloring is not printed. Also see Use Syntax Color in Editor.
Load Defaults | Reset the syntax color table back to the default color scheme.
Assembler | Launch the color selection dialog to choose the text (foreground) and background colors for inline assembler code.
Comments | Comments and REM statement syntax color.
Keywords | The syntax coloring applied to reserved keywords.
PB Forms | The coloring applied to PowerBASIC Forms™ named-block metastatements. Note: PowerBASIC Forms™ is a GUI visual design tool, and therefore IDE support for it is currently restricted to the PowerBASIC for Windows product line. In the Console Compiler's IDE, the PB Forms syntax option is disabled, and reserved for future use.
Strings | The syntax coloring applied to literal strings.
Text | The remaining types of syntax. Typically, this includes variable names, API function names, etc.
Selection | The color used when selecting (highlighting) blocks of text, for example, in anticipation of clipboard operations such as Cut/Copy/Paste, etc.
Breakpoint | The color used to highlight a breakpoint.
Bookmark | The color used to highlight a bookmark.
Exec point | The color used to highlight the execution point, which is the next line to be executed in the debugger.


Syntax Color Selector

Select the Text color | The list contains a set of the 16 standard colors, plus the current custom text color. To choose a color in the list, simply select (click) on it. To adjust the custom text color, select the Create a Custom color button. The top-right preview control shows the current color combination as it will appear in the editor window.
Select the Back color | The Back color list box control functions identically to the Text color list box control, but adjusts the background color rather than the text color.
[Preview Fore/Back] | Preview of the current color combination and the RGB color parameters required to display the colors.
Create a Custom color | The launches the Syntax Custom Color Selector dialog.
OK | Accept the current text and background color selections, and return to the Options dialog.
Cancel | Abort the Syntax Color Selector dialog without making any changes to the color settings.


Syntax Custom Color Selector

[Color map] | A color map based on the current display color-depth, to facilitate easy selection of custom colors. To choose the text color, use the left mouse button and click on the desired point in the color map. Use the right mouse button to choose the background color. The border of the color map provides a convenient set of linear (static) color gradients to assist in choosing colors that are more "pure". The current text and background combination is shown in the top-right preview control, as it will appear in the editor window.
[Scroll bar] | The scrollbar adjusts the "white saturation" level of the color map. Moving the scroll bar upward adds more whiteness to the color map colors, whereas moving the scrollbar downward reduces the whiteness of the color map colors. The default is 50% white saturation. Changing the white saturation level does not affect the gradient border around the color map.
[Preview Fore/Back] | Preview of the current color combination and the RGB color parameters required to display the colors.
[Current RGB] | Indicates the RGB color under the mouse cursor in the color map.
[Saturation] | Indicates the "white saturation" level of the color map. This is adjusted with the scroll bar located to the right of the color map
OK | Accept the current custom text and background color selections, and return to the Color Selector dialog.
Cancel | Abort the Syntax Custom Color Selector dialog without making any changes to the custom color settings.


Debugging PB/CC Programs

Once your code is written, the next step is to test it to make sure it performs according to specifications. Regardless of the computer language used, certain programming errors are common: misspelled or misused variables, inverted logical tests, mistakes in syntax, and "reasonable" tests that cause disastrous failures when unreasonable data is supplied. Each language also has its own common errors, unique because of the peculiarities of its language.

Some issues to be aware of include the free conversion of most numeric data types, side effects of global variables, default data types, and overuse of GOTO causing problems with incorrect branching. These are well known to the experienced BASIC programmer but are not generally found in other languages.

The PB/CC Integrated Development Environment (CCEDIT.EXE) can be used to find and correct both general programming errors and errors specific to BASIC. Nearly every program has bugs at least at first. To find them, you may need to check any statement in the program, display the value of any variable, and observe the program flow from line to line. CCEDIT has all these capabilities and more.

This section explains how to use CCEDIT to find and fix errors in a sample program, by providing a list of the debugging commands, a description of each, and then showing how each is invoked. If you follow certain guidelines when creating your program, you will find debugging easier (and less necessary). The procedures we describe here will help you form your own set of guidelines that will make your programs easier to write and maintain.

See Also

How the integrated debugger works

The DEBUG Menu

Debugging a simple program

The Integrated Development Environment


How the integrated debugger works

The integrated debugger works in conjunction with the PowerBASIC editor and is a part of the PowerBASIC environment. The debugger allows you to debug at the PowerBASIC level rather than at the machine level. That makes it a source-level debugger.

To debug a PowerBASIC program using the integrated debugger, first load the program into the editor and choose Compile and Debug from the toolbar or menu. Your program will be compiled, and if there are no compile-time errors, it will begin executing.

Breakpoints are places where the program will stop. In most cases, you will want to set one or more breakpoints in your program. The program executes up to (but not including) the line containing the breakpoint and then passes control of the debugger over to you. Breakpoints that you set remain in place until you clear them or exit the IDE.

Once at a breakpoint you can:

· Display the value of a variable (with the Evaluate Variable button or menu item)

· Set up a list of variables (in the Variable Watcher window) and see how their values change as the program executes

· Clear breakpoints, set new ones, or both

· Single-step the program (run it one line at a time)

· Run the program to the next breakpoint

See Also

Debugging PB/CC Programs

The DEBUG Menu

Debugging a simple program


The DEBUG Menu

The Debug Menu provides the essential tools for debugging a PowerBASIC program. We will run through these in their order of appearance:

Run | Begin running the program. It will continue to run until the debugger either encounters a breakpoint, or runs out of code to execute. F5 is the hot-key for the Run option.
Run to Caret | Begin running the program. It continues to run until the debugger either reaches the current line, or encounters a breakpoint, etc. CTRL+F8 is the hot-key for the Run to Caret option.
Animate | The debugger runs the program using an automated Step-Into technique. Execution continues until a breakpoint is reached, the Stop button is pressed, or the program completes. The Animate delay can be set through the IDE's Options Dialog.
Stop | Halt the debugger. If the debugger is already halted, this has no effect. CTRL+BREAK is the Stop hot-key.
Step Into | If the current line contains a call to a Sub or Function, the debugger traces execution into that Sub or Function. You cannot step into an API call, or into an external module. F8 is the Step Into hot-key.
Step Over | The debugger executes the current line of code. If the line contains a reference to a Sub or a Function, the debugger executes that code without tracing into the Sub or Function. SHIFT+F8 is the Step Over hot-key.
Step Out | The debugger runs the code until the current Sub or Function exits. If the current function is PBMAIN or WINMAIN, the code is executed until the program is finished or another breakpoint is encountered. CTRL+SHIFT+F8 is the Step Out hot-key.
Evaluate Variable | Evaluate or modify a variable, or add/remove a variable in the Watch window. It is not possible to use this to change the length of a string. Also see Watch CPU Registers.
Clear all Watches | Remove all variables from the Watch window.
Toggle Breakpoint | Set or release a breakpoint on the current line. F9 is the Toggle Breakpoint hot-key.
Clear all Breakpoints | Release all breakpoints in the program.
Console on Top | Set or release the permanent "Always on Top" or "topmost" property of the console window created by the debugger. When the console is not permanently topmost, it can be temporarily brought into the foreground (above the debuggers windows) by the Console button on the toolbar.
Watch CPU registers | Show or hide the Register Watcher window, which lets you see the state of the CPU registers and flags when debugging.
Variable watch window | Show or hide the Variable Watcher window, which lets you see the state of the ERR function and any variables you choose to watch when debugging.
Program Reset | If the current program is halted/stopped, the program will be reset, ready for debugging to commence again. SHIFT+F5 is the Reset hot-key.
Exit Debugger | Halts the current program and terminates the debugger. The variable list in the Watch window is retained between debugging sessions, until the IDE is closed.


Debugging a simple program

For our first example, we'll use a simple program designed to read a text file and display it. Along the way, the program counts the number of words and tabulates the lengths of all words found - how many words are one character long, how many are exactly two characters long, and so on. The sample program, TWORD.BAS, contains a number of bugs; you will be using the PowerBASIC integrated debugger to find each of them.

Be sure to make copies of the TWORD.DAT data file; TWORD.BAS reads that file and makes specific errors because of the data. While another data file may work as well, it is possible that one or more of the bugs will not occur if you use a different data file.

Here is a listing of the TWORD.BAS program.

When you have loaded TWORD.BAS into the editor, click on the Debugger button on the toolbar, or select Run from the menu, then Compile and Debug.

At this point, the debugger will have scrolled the program and highlighted the line containing the definition of the variable MaxWordLen, since that will be the first line executed when the program begins to run. The highlight is called the execution bar and marks the line of code at the execution position. In other words, that line will be executed next.

To make the program run, click on the Run button in the toolbar or press F5. The program's output appears in the User screen, which allows you to see how the program would look if you weren't using the debugger. If the User screen is not visible you may have to select it by using the Windows Taskbar, ALT+TAB, or by re-sizing the PowerBASIC IDE to a smaller size and different location until the User screen is visible. TWORD prompts you for the name of the file to read. Enter TWORD.DAT and press ENTER. TWORD displays the first line of the file then locks up because of one of the bugs in the program. To regain control, click on the Stop button or press CTRL+BREAK. You can choose Program Reset (or press the SHIFT+F5 hot-key) to quit running the flawed program. Clicking the Run button lets you restart the program.

Next See: Setting and using breakpoints

See Also

Debugging PB/CC Programs

How the integrated debugger works

The DEBUG Menu

The Integrated Development Environment


TWORD.BAS

'==========================================================

'

' Test Word example (TWORD.BAS)

' Copyright (c) 1998-2006 PowerBASIC, Inc.

' All Rights Reserved.

'

' Read a text file and count the number of words of length

' 1, 2, 3, and so on. This program contains intentional

' bugs. Use it in conjunction with the PowerBASIC Users

' Guide to learn the PowerBASIC integrated debugger.

'

'==========================================================

#IF NOT %DEF(%WINAPI)

DECLARE FUNCTION GetModuleFileName LIB "KERNEL32.DLL" _

ALIAS "GetModuleFileNameA" (BYVAL hModule AS DWORD, _

lpFileName AS ASCIIZ, BYVAL nSize AS LONG) AS LONG

#ENDIF

DEFLNG A-Z

FUNCTION AppPath() AS STRING

LOCAL p AS ASCIIZ * 256

LOCAL ix AS LONG

GetModuleFileName 0, p, SIZEOF(p)

FOR ix = LEN(p) TO 1 STEP -1

IF MID$(p, ix, 1) = "\" OR MID$(p, ix, 1) = "/" THEN

FUNCTION = LEFT$(p, ix)

EXIT FUNCTION

END IF

NEXT

FUNCTION = ""

END FUNCTION

FUNCTION PBMAIN() AS LONG

MaxWordLen = 16 'count words up to 16 chars

'longer words go into Overlong

DIM WordLength(MaxWordLen) 'the array used to hold counts

Blank$ = CHR$(32) 'a space marks end of a word

FilePath$ = AppPath

IF LEN(FilePath$) THEN

CHDRIVE FilePath$

CHDIR FilePath$

END IF

PRINT "Warning: This is a program intended for use in a"

PRINT "practice session of the PowerBASIC debugger."

PRINT "If you are NOT running this program in the "

PRINT "integrated debugging environment, press CTRL+BREAK"

PRINT "now!"

PRINT "See the Debugging chapter in the PowerBASIC Users"

PRINT "Guide for details."

PRINT

WHILE InFile$ = ""

LINE INPUT "Enter the name of the input file: ";InFile$

IF InFile$ <= SPACE$(LEN(InFile$)) THEN InFile$=""

IF InFile$ = "" THEN

BEEP

PRINT "TWORD canceled!"

EXIT FUNCTION

END IF

WEND

OPEN InFile$ FOR INPUT AS #1

'If the file can't be opened, give user an error message.

IF ERR THEN

PRINT "Unable to open file "; InFile$

EXIT FUNCTION

END IF

WHILE NOT (EOF(1)) 'read file until nothing left

LINE INPUT #1,FirstString$ 'get a line

PRINT FirstString$ 'display it

WHILE FirstString$<>""

GOSUB GetAWord 'pull word for FirstString$

'put it in SecondString$

Test = LEN(SecondString$)

IF Test <= 16 THEN

WordLength(Test) = WordLength(Test) + 1

ELSE

Overlong = Overlong + 1

END IF

WEND

WEND

CLOSE 1

PRINT "Length Count"

FOR Count% = 1 TO 16

PRINT Count%, WordLength(Count%)

NEXT Count%

PRINT "Greater"; OverLong

WAITKEY$

EXIT FUNCTION

GetAWord:

position = INSTR(FirstString$, Blank$) 'a word is a

' sequence of characters ended by a blank or

' the end of the line

IF position = 0 THEN

'the word is the remainder of the line

SecondString$ = FirstString$

FirstString$ = ""

ELSE

'pull the word from the line

SecondString$ = LEFT$(FirstString$, position - 1)

END IF

RETURN

END FUNCTION


Setting and using breakpoints

We know the program did not fail within the first few lines; it requested the name of the input file, and it successfully opened that file. Therefore, the problem must have been caused by something further along in the code.

The first suspicious line concerns the GetAWord subroutine. Set a breakpoint at the line reading:

position = INSTR(FirstString$, Blank$)

Use the arrow keys to move to that line. As you do, you'll notice that the execution bar doesn't move. That is because you are not executing the program; you are just using the source browser to move within the program source.

To set a breakpoint at the line to which you moved, double-click on it or press the F9 key. The line is highlighted, indicating that the breakpoint has been set. If you wanted to remove the breakpoint at that line, double-click on it or press the F9 key again. The breakpoint highlighting differs from the execution highlighting, and this difference helps you to avoid confusion over highlighted breakpoints and the current program position.

Once more, click on the Run button (or press F5). TWORD starts running again, and things happen just as before, with one exception: after the first line from the data file has been displayed on the User screen, TWORD halts and waits for further commands. PowerBASIC has reached the breakpoint. The caret and the execution bar are on the line containing the breakpoint.

The breakpoint line cannot be doubly highlighted, so the execution bar obscures the breakpoint highlighting until the program executes further. The program stops each time it reaches the breakpoint line.

You can also stop a program running within the debugger by pressing CTRL+BREAK. When you do this, the program executes the current line and stops at the beginning of the next. Control is then returned to the PowerBASIC debugger, and the execution bar highlights the next line to be executed. You may now use debugger commands to step through the program or resume execution.

Next See: Tracing execution

See Also

Debugging PB/CC Programs

How the integrated debugger works

The DEBUG Menu

Debugging a simple program


Tracing execution

Now that you have executed TWORD to the first breakpoint, you can trace the execution one line at a time by pressing F8 or by clicking on the Step-Into button. When you press F8, the debugger runs the execution line and stops at the beginning of the next line.

If the program produced any screen output, you can check it by clicking on the Console on Top button. Since the program displays the first line of text from the data file, the bug is probably not in the screen output.

Perhaps there is something wrong with that INSTR function call? You could not check the value of the variable position while the breakpoint line was highlighted, because the breakpoint line had not yet executed. After pressing F8 and executing the breakpoint line, the value of the variable position should be known. The value of position is critically important, so let's check it.

Next See: Evaluating a variable


Evaluating a variable

To see the value of position, click on the Evaluate toolbar button, or right-click on the variable and choose the Evaluate variable item from the context-menu - since this is a context-sensitive menu, it will actually read "Evaluate position". In either case, the opens the Evaluate dialog containing two data entry fields. The variable name position should be automatically filled in the Variable Name field, and the content of the variable shown in the Value field. In this case, position is expected to contain 3, and it does. There is no error so far. To return from the pop-up window to the main part of the debugger, click on the Close button or press ESCAPE.

The next few lines are supposed to remove the word from the beginning of the line and put the word into SecondString$. To check if that routine functions correctly, you should examine the values of FirstString$ and SecondString$ before and after the routine alters them. The debugger should display:

To be or not to be; that is the question.

From the appearance of the string, you can see that the first blank should appear in position 3, and that the program has correctly determined that position. The variable SecondString$ ought to contain the last word processed and should have no value yet. You can check that by entering SecondString$ in the Variable Name field; if you do, you will find no error.

Everything seems normal so far. Press ESCAPE to return to the main part of the debugger, then press F8 to step the program one more line. Since you already know position is not 0, you'll find out what you need to know by pressing F8 several more times, stopping when the execution bar is over the final END IF of that routine. At that point, both FirstString$ and SecondString$ have been processed.

Once more, evaluate SecondString$. This time, SecondString$ does contain data: the word "To". This seems correct. When you ask to see the value of FirstString$, though, you get a surprise: FirstString$ has not been changed at all! This explains the lockup for the first line; subroutine GetAWord was correctly supplying the first word, but was not removing that first word from the entry string. Therefore, the first line never actually became shorter, so it was being processed and reprocessed endlessly.

To correct the bug, exit the debugger and insert a routine in the editor that shortens FirstString$ by the length of SecondString$. Insert a line reading something like:

FirstString$ = MID$(FirstString$, position + 1)

…immediately before the END IF in GetAWord. Make this correction, then save it. Click on the Compile and Debug icon in the toolbar and run the program through the debugger again.

Next See: Summary

See Also

Debugging PB/CC Programs

Debugging a simple program

Setting and using breakpoints

Tracing execution


Debugging TWORD.BAS Summary

TWORD fails because the program goes into an infinite . The infinite loop was caused by the fact that the number of characters removed was not shortening the input .

While tracking down this bug, you learned to:

· Set and use breakpoints

· Run a program without stopping at each line

· Step through your source one line at a time

· Evaluate the values of variables

See Also

Debugging PB/CC Programs

How the integrated debugger works

The DEBUG Menu

Debugging a simple program


The PowerBASIC COM Browser

The PowerBASIC COM Browser is an application that exposes the Interfaces, Methods, and Properties of COM Objects, as described by type-library files. The PowerBASIC COM Browser serves a dual purpose - to export an Interface Structure of a COM Object for early-binding purposes in your PowerBASIC code, and to provide syntax reference and context-help on the Interface members exposed by a COM Object.

If you are unfamiliar with COM Automation programming, you may wish to review the COM Programming chapter to gain an insight into COM programming concepts before reading this topic.

The PowerBASIC COM Browser can be launched from the Tools menu in the PowerBASIC IDE, launched as a stand-alone application by double-clicking PBROW.EXE in the \PB\BIN\ folder, or run from the command-line by typing PBROW.EXE (and then press ENTER).

When launched, the PowerBASIC COM Browser offers a straightforward user interface, with which you open specific type-library files or choose from a list of registered libraries.

Before we start, we should first clarify a few terms to avoid confusion:

COM Object | An instance of an initialized COM library or application. COM Objects usually come in EXE (out-of-process), and DLL, or OCX formats (in-process). These discussions pertain to COM libraries that act as COM Servers, regardless of whether they are in-process or out-of-process Servers.
type-library | A type-library is a file that contains a database or data dictionary describing the Interfaces and Interface members exposed by a COM Object.

See Also

COM Programming Introduction

The PowerBASIC COM Browser user interface

Opening a type-library

Exporting an Interface Structure

Additional Notes

The PowerBASIC COM Browser Tutorial

Early Binding Tutorial

Late Binding Tutorial


The PowerBASIC COM Browser user interface

From the definitions of terms above, we can see that a type-library is used to describe a COM Object. Before describing the operation of PowerBASIC COM Browser, we will review its user interface elements.

Type Library | The name of the type-library currently being examined. This is a read-only control, but the content can be copied to the clipboard for pasting into application code for reference or versioning purposes. The type-library name is not used by PowerBASIC itself.
ProgID | An alphanumeric program identifier string that uniquely identifies a COM Object. This identifier is recorded in the Windows Registry when the COM Object is installed onto a PC, using the REGSVR32.EXE utility that comes with Windows. Interfaces marked with an asterisk (*) are termed "default" Interfaces. In program code, access to other Interfaces of the Object must be performed through the default Interface.
Interface Prefix | An optional alphanumeric string used to prefix all Interface Structures exported by the PowerBASIC COM Browser. By default, the PowerBASIC COM Browser creates a default prefix based on the library name, but you may edit or clear this prefix completely. When using more than one COM Object in an application, the use of a prefix can clearly distinguish to which Object an Interface is associated.
Interfaces | This list contains all the Interfaces exposed by the COM Object. Default interfaces are presented at the top of the list and are marked with an asterisk symbol (*), the remaining Interfaces (if any) follow. Interfaces contain members that are classed as either Methods or Properties. Finally, enumeration identifiers appear at the bottom of the list, prefixed with "[Enum]". Enumerations are essentially a list of numeric or string equates that hold special meaning to the COM Object, and are commonly used to specify parameter values.
Methods/Properties | The member portions of a selected Interface appear in this list, as do members of enumeration identifiers.
Syntax | The Syntax control contains the PowerBASIC compatible syntax of the Interface member highlighted in the Methods/Properties control. The Syntax control is read-only, but text may be copied to the clipboard for pasting into application code. In general, this control is used for syntax reference purposes. The PowerBASIC COM Browser offers alternative (simpler) ways of copying Interface definitions to the clipboard.
? | When enabled, the context-help button can be launched to provide context-help on a given Method or Property of an Interface. This button is only enabled if the type-library references a help file associated with the COM Object.

See Also

The PowerBASIC COM Browser

Opening a type-library


Opening a type-library

The PowerBASIC COM Browser supports opening a type-library in two ways through the File menu, or via keyboard accelerators:

Open Registered Library | When selected via the menu (or using the CTRL+R accelerator key combination), the PowerBASIC COM Browser examines the Windows registry and presents a list of all registered type-libraries. This includes COM Servers, but also includes other type libraries such, as ActiveX Controls, that are not necessarily supported by PowerBASIC. The Browser warns you if you try to open an incompatible type-library.
Open Library File | This presents a standard "Open File" Dialog box. By default, the PowerBASIC COM Browser only shows *.TLB and *.OLB files (the standard file extensions for type-library files), but also provides for opening Object files directly too. Valid Object files are usually named *.DLL, *.EXE, or *.OCX. The PowerBASIC COM Browser supports opening other file types too, in case standard naming conventions have not been used for the file name. However, the PowerBASIC COM Browser will attempt to validate the file as a type-library or Object file as it is opened, and warns if the file is incompatible or does not contain type-library information.

The PowerBASIC COM Browser cannot open type-library or Object files that do not have at least one Interface that has a ProgID, indicating they support the Dispatch Interface (IDispatch) or are Dual-interface Objects. Without a ProgID, a COM Object will not support the Dispatch Interface. COM Objects of this variety often expose "direct" interfaces, and may be ActiveX Controls rather than COM servers. Objects of this type are not compatible with PowerBASIC (and are often not compatible with Visual Basic too).

See Also

The PowerBASIC COM Browser

Exporting an Interface Structure

COM Programming Introduction


Exporting an Interface Structure

The PowerBASIC COM Browser supports exporting an Interface Structure to the clipboard or directly to a disk file. Both options are available through the File menu, plus options to Find and Select All Interfaces:

When the PowerBASIC COM Browser exports data, it generates a set of compilable PowerBASIC INTERFACE structures that define the selected Interface(s) and Interface members. Enumerations in the list are also exported as a list of numeric and/or string equates. In addition, the PowerBASIC COM Browser will add a header block noting the name and other relevant details from the type library.

When developing a COM controller application, it is common for all the Interfaces to be exported to a .INC file which is included into the main application with a #INCLUDE metastatement. However, exporting to the clipboard can be useful in situations where just one or two Interface definitions are required in the program.

Including the entire Interface definition and Enumerations for a COM Object only increases the size of the source code -- it has no impact on the size of the final compiled code.

The following is a portion of the data exported by the PowerBASIC COM Browser:

' ----------------------------------------------------------

' Library Name: Microsoft Word 8.0 Object Library

' Library File: C:\Office\MSWORD8.OLB

' ----------------------------------------------------------

' Version Info:

' -------------

' Company Name: Microsoft Corporation

' File Description: Microsoft Word for Windows® 97 object library

' File Version: 8.0

' Internal Name: mswordtlb

' Legal Copyright: Copyright (C) Microsoft Corporation 1983-1996.

' Original Filename: MSWORD8.OLB

' Product Name: Microsoft® Word for Windows® 97

' Product Version: 8.0

' ----------------------------------------------------------

' ProgID: Word.Application.8

' Interface Name: WordApplication

'

' Interface Prefix: Word

' ----------------------------------------------------------

$PROGID_WordApplication8 = "Word.Application.8"

INTERFACE DISPATCH WordApplication

MEMBER GET Application<&H3E8>() AS WordApplication

MEMBER GET Creator<&H3E9>() AS LONG

MEMBER GET Parent<&H3EA>() AS VARIANT

MEMBER GET Name<&H0>() AS STRING

MEMBER GET Documents<&H6>() AS WordDocuments

...

See Also

The PowerBASIC COM Browser

COM Programming Introduction

What is a Dispatch Interface?

Creating an Instance of an Object

Enumerating collections

Example COM controller application


Additional Notes

The shortcut keys (CTRL+A for Select and CTRL+C for Copy) perform their respective functions in the read-only text boxes when focus is within one of the text boxes (this is useful for copying file and library names for use in application code). Otherwise, the shortcut keys activate the respective functions on the File pull-down menu.

The Find Interfaces on the File pull-down menu (and the Find menu item in the Interfaces list box context menu) allow you to quickly find a specific Interface within the Interfaces list box.

Upon choosing one of the menu options or by pressing CTRL+F, a Find Interface dialog box appears just above the Interfaces list box. Type the name of the Interface or Enumeration that is to be located.

Please note that there are two options available in the Find Interface dialog: Select found interface and Scroll to found interface. As the name of the Interface is typed into the Find Interfaces edit box, the "found" results are scrolled into view in the Interfaces list box.

If the Select found interfaces option is chosen, the first matching Interface is automatically selected by the PowerBASIC COM Browser; otherwise, the matching Interface is simply scrolled into view. This is helpful when you want to find an Interface without changing your current selections.

To stop the search, press ENTER or click the CLOSE button. When the Find Interface dialog is dismissed, the "found" item receives focus.

See Also

The PowerBASIC COM Browser

The PowerBASIC COM Browser user interface

Opening a type-library

Exporting an Interface Structure

The PowerBASIC COM Browser Tutorial

Late Binding Tutorial

Early Binding Tutorial

COM Programming Introduction


The PowerBASIC COM Browser Tutorial

As described in the previous topic, the PowerBASIC COM Browser is a browser utility application that exposes the Interfaces, Methods, and Properties in a type-library. It is also used to generate an Interface structure that may be inserted into your PowerBASIC source code for use with early-binding.

Early-binding is often preferred over late binding when creating OLE Automation applications, since it offers superior performance. Regardless of whether early-binding or late-binding is used, the PowerBASIC COM Browser shows the syntax to use. If a type-library refers to a Help file, the PowerBASIC COM Browser can display the appropriate Interface help topic upon request.

If you are unfamiliar with COM Automation programming, you may wish to review the COM Programming chapter to gain an insight into COM programming concepts before using this tutorial.

Let's suppose we want to create an application that:

1. Starts Microsoft Word 97

2. Opens a new document window

3. Enters some text

4. Saves the file

5. Closes the document

6. Ends Word

Assuming we have no idea how to start writing code for this project, the best place to begin is to record a macro in Microsoft Word that performs much the same steps as we want our program to perform. In Word, we can record a macro that performs steps 2-5. Starting and closing Word is the only part that we cannot record (since Word must be running to run the macro!).

We can follow this same procedure with other OLE Automation programs. We'll use Word 97 in this example simply because it is a commonly available application.

In this tutorial, we'll first use the PowerBASIC COM Browser to find the proper syntax to write our application using late binding. After that example is done, we'll change the code to use early binding.

See Also

The PowerBASIC COM Browser

The PowerBASIC COM Browser user interface

Opening a type-library

Early Binding Tutorial

Late Binding Tutorial

COM Programming Introduction


Early Binding

Early binding differs from late binding in two very important ways:

1. Early binding allows the compiler to perform type checking.

2. Early binding knows the Dispatch ID so it's more direct and faster.

For the most part, the syntax for early binding is exactly the same as that for late-binding. All that is needed is to let PowerBASIC know that early binding should be used instead of late binding. This is done by simply changing the keyword DISPATCH to the desired Interface. In our source code, we change the following:

DIM oWord AS DISPATCH

LET oWord = NEW DISPATCH IN "Word.Application.8"

to:

DIM oWord AS WordApplication

LET oWord = NEW WordApplication IN "Word.Application.8"

The only other change is to specify all the needed Interfaces and their respective members. This is called Interface Structure, and this is where the PowerBASIC COM Browser becomes extremely useful - trying to create the Interface Structure without the PowerBASIC COM Browser is a complex and tedious task.

Usually it is quickest to just include every possibly Interface and Enumeration in our code, regardless of whether we need to use them all. Including the entire Interface definition and Enumeration only increases the size of the source code, and has no impact on the size of the compiled code. We can do this using the following technique:

1. Choose File | Select All Interfaces from the pull-down menu.

2. Choose File | Save Interface to File from the pull-down menu.

3. Enter a name, such as WORD.INC, and choose Save. Word 97's Automation Interface structure is quite large, so it may take a few seconds to enumerate and save.

4. Type the following at the top of your source code:

#INCLUDE "WORD.INC"

5. Compile and Run the code.

That is all there is to it…!

Multiple Interfaces and Enumerations can be copied to the clipboard by holding down the CTRL key as each item is selected. A range can be selected by selecting the first item then while holding down the SHIFT key, select the last item in the range - all items in the range will be highlighted.

Finally, choose the File | Save Interface to Clipboard menu item from the pull-down menu or right-clicking the Interfaces list box and choose Copy from the context menu.

For additional information on using the PowerBASIC COM Browser utility, please see The PowerBASIC COM Browser User Interface topic.

See Also

COM Programming Introduction

The PowerBASIC COM Browser

Early-binding and Late-binding

Late Binding Tutorial

How does the LET statement work?


Late Binding

Start the PowerBASIC COM Browser by executing PBROW.EXE. The very first time that the PowerBASIC COM Browser is launched, no type-library will be selected. However, in all subsequent launches, the PowerBASIC COM Browser opens the last type-library that was open when the PowerBASIC COM Browser was last closed.

To open a type-library, we choose File | Open Registered Library from the pull-down menu. Next, we select "Microsoft Word 8.0 Object Library" from the list of available type-libraries, then choose Open.

The name of the type-library appears in the (read-only) Type-Library text box. The Program ID also appears in a read-only text box. The read-only text boxes make it convenient for us to copy the Program ID and paste it into our source code using standard copy/paste techniques. For example, right-click to bring up the associated context-menu for the text box, or set keyboard focus to the text box and press CTRL+A to select and CTRL+C to copy the ID to the clipboard.

Now we go to the PowerBASIC IDE and open a new document, where we type the following code:

FUNCTION PBMAIN

DIM oWord AS DISPATCH

LET oWord = NEW DISPATCH IN "Word.Application.8"

LET oWord = NOTHING

END FUNCTION

Remembering that we copied the Program ID ("Word.Application.8") to the clipboard just before, we can paste that into the code as we type. What we have just done is create a program that will start a new instance of Microsoft Word 97.

If we left the NEW clause out of the LET statement, the object variable oWord will refer to the first running instance of Microsoft Word 97 that is found, if it can be found at all.

We can use OBJACTIVE to help determine if an out-of-process COM Object (EXE) is active or not, or we can use the LET statement (without the NEW clause) and rely on run-time error checking to determine success or failure).

We can save, compile, and then run the program as it stands, and it will work. Of course, it may not appear to work, since Word will probably run fine, but will not be visible to us! We can check that it is running by looking at the process list using Window's Task Manager utility (Windows NT/2000/XP) or any utility that displays an active process list.

Now we will add code to make Word visible. We go back to the PowerBASIC COM Browser and see if we can find some Method or Property that would make the application visible. In the Interfaces list box, we select the Application Interface.

Notice that the Methods/Properties list box will fill with all the available Methods and Properties of the Application Interface. Also notice that the "?" button above the Syntax text box will be enabled if the COM Interface library has an associated help file. We can view the help by clicking the ? button, or by choosing Help on the PowerBASIC COM Browser menu, then Help on Library's Topic from the pull-down menu, or simply by pressing the F1 key.

You will note that the Help file for Word 97's Object Library is written for Visual Basic users, so while the syntax is not exactly the same for PowerBASIC, it is very similar. If we read the information in the Help file on the Application Object, we see that the example shows how to make Word visible. The key word here is Visible.

Now we examine the Methods/Properties list box, and locate Visible. There will be two listed. Examining one then the other, we see that one Visible is a LET and the other is a GET. That means Visible is an Interface Property.

At this point, we define some terminology just to make sure we are clear:

Interface | Contains Methods and Properties to be used by an application (client/controller) to communicate with a COM Object.
Object | An instance of a COM Object Interface.
Method | An action that an object can perform, similar to a Sub/Function
Property | An attribute of an object. A Property is similar to a conventional variable.

Properties may be specified as LET or GET (and occasionally as a SET). LET is used for setting/writing the value of a Property, and GET is used for getting/reading the value held by a Property. Interface Methods are always accessed by a CALL.

All Interface parameters and return values are passed using Variants. The syntax shown by the PowerBASIC COM Browser indicates the type of data that is expected to be stored within the Variant.

In the case of unnamed parameters, the syntax does not show a parameter, yet one is expected by the COM Object. In this case, the parameter type is usually displayed in a comment to the right of the syntax. The number within the angled brackets "<..>" is the Dispatch ID, which is used by the compiler for early-binding. Since the number must be accurate, it is a good idea to use the PowerBASIC COM Browser to generate the code. We'll return to early binding later in this tutorial.

With either Visible Property selected in the Methods/Properties list box, we can press F1 to display the Help for the Visible Property. This tells us that in order to make Microsoft Word visible, we set the Application.Visible Property to True.

With Variants, we can set the Property to True by assigning it the number 1, or by assigning the literal text "True". For example:

DIM vBool AS VARIANT

vBool = 1

…or we can use:

vBool = "True"

Both vBool assignments perform the same task when the Property is expecting a Boolean value. Since dealing with numeric values is almost always faster than dealing with strings, we will give preference to the use of numbers. Now we edit our code to look like the following:

FUNCTION PBMAIN

DIM oWord AS DISPATCH

DIM vBool AS VARIANT

LET oWord = NEW DISPATCH IN "Word.Application.8"

vBool = 1

OBJECT LET oWord.Application.Visible = vBool

LET oWord = NOTHING

END FUNCTION

We can drop the word "Application" in the "OBJECT LET oWord.Application.Visible = vBool" statement as it is the Object's "global" Interface. This reduces the OBJECT LET statement so it appears as follows:

OBJECT LET oWord.Visible = vBool

Again, we can learn all this information from the Word 97 Automation Help file (See "Global Object"). Now we will compile and run the modified application code to check that it continues to work fine.

Now we'll return to the macro we mentioned earlier. If you are unfamiliar with recording macros, please see "macros, creating" or " Recording a macro to generate code" in the Word 97 Help file. When we recorded our macro to perform the steps we mentioned earlier, this is what Word 97 saved:

Documents.Add Template:="Normal", NewTemplate:=False

Selection.TypeText Text:="PowerBASIC controls Microsoft Word!"

Selection.TypeParagraph

ActiveDocument.SaveAs FileName:="Test.doc", _

FileFormat:=wdFormatDocument, _

LockComments:=False, Password:="", _

AddToRecentFiles:=True, WritePassword :="", _

ReadOnlyRecommended:=False, _

EmbedTrueTypeFonts:=False, _

SaveNativePictureFormat:=False, _

SaveFormsData:=False, _

SaveAsAOCELetter:=False

ActiveWindow.Close

Now we now know the basics techniques for finding Methods and Properties, we can find each of the above Interfaces and their respective Methods. Documents and Selection are both interfaces found in the Interfaces list box in the PowerBASIC COM Browser. ActiveDocument and ActiveWindow are not.

If we select Application in the Interfaces list box, we notice that ActiveDocument and ActiveWindow are both Properties of the Application object.

We select each of these and notice in the Syntax text box that ActiveDocument returns a Document object, and ActiveWindow returns a Window object. To get further information, we need to select the Document and Window Interface respectively.

Stepping back for a moment, we can see that it is possible for an Interface member to return an object that refers to another Interface in the Object. This is an important point when using Interfaces - in most cases, one Interface provides the "mechanism" to use the other Interfaces exposed by a COM Object.

Continuing with the example code, we will convert the above Word macro code to PowerBASIC, giving us the following code:

%wdFormatDocument = 0

FUNCTION PBMAIN

DIM oWord AS DISPATCH

DIM vBool AS VARIANT

DIM vText AS VARIANT

DIM vFile AS VARIANT

DIM vFileFormat AS VARIANT

LET oWord = NEW DISPATCH IN "Word.Application.8"

vBool = 1

OBJECT LET oWord.Visible = vBool

OBJECT CALL oWord.Documents.Add

vText = "PowerBASIC controls Microsoft Word!"

OBJECT CALL oWord.Selection.TypeText(vText)

OBJECT CALL oWord.Selection.TypeParagraph

vFile = "Test.doc"

vFileFormat = %wdFormatDocument

OBJECT CALL oWord.ActiveDocument.SaveAs(vFile, vFileFormat)

OBJECT CALL oWord.ActiveWindow.Close

OBJECT CALL oWord.Quit

LET oWord = NOTHING

END FUNCTION

Since some of the parameters are optional, we can omit them provided they use the default value. Default values can be identified through the Object's documentation.

From the code above, you may be wondering where the %wdFormatDocument equate came from. When examining the Help file for the SaveAs Method in the Document Interface, we will note that the FileFormat parameter can be one of the WdSaveFormat constants. Switching back to the PowerBASIC COM Browser, we can select WdSaveFormat in the Interfaces list box, and in the Methods/Properties list box, we are presented with a list of constants/values that are available for the FileFormat parameter.

The items in this list are called Enumerations, and these items are prefixed with "[Enum]" in the Interfaces list box to clearly distinguish them from Interfaces.

Select the desired constant in the Methods/Properties list box. Then, you will be able to copy the value from the Syntax text box. The PowerBASIC COM Browser helps make this process of "discovery and use" very straightforward.

Notice that we also added the Quit call at the end of the above code. This terminates Microsoft Word. At this point, we can compile and run the above code. Microsoft Word will briefly flash up on the screen and then disappear.

To verify that the program code worked as intended, we can open Microsoft Word and then open the TEST.DOC document and check that Word did exactly what it was instructed to do - fill in the text "PowerBASIC controls Microsoft Word!".

See Also

COM Programming Introduction

The PowerBASIC COM Browser

Early-binding and Late-binding

Early Binding Tutorial

How does the LET statement work?


Numbers

Data Handling

The care of numbers constitutes an important part of every programming system. Fortunately, PowerBASIC allows you to ignore most technical considerations about internal number handling. If you never give a thought to such matters as calculation speed, precision, and memory requirements, your programs will usually continue to work as you expect. However, an understanding of the underlying issues will help when you need to write programs that are faster, more accurate, and require less memory.

For efficiency, PowerBASIC stores and processes data in different forms. It supports eleven unique numeric types, three string types, and also pointers. The following tables summarize the most important features and distinctions of these data types. The rest of this section explains these features in detail.

Numeric Data storage requirements and ranges

Data Type | Size | Decimal Range | Binary Range
Integer | 16 bits (2 bytes), signed | -32,768 to 32,767 | -2^15 to 2^15-1
Long-integer | 32 bits (4 bytes), signed | -2,147,483,648 to 2,147,483,647 | -2^31 to 2^31-1
Quad-integer | 64 bits (8 bytes), signed | -9.22*10^18 to +9.22*10^18 | -2^63 to 2^63-1
Byte | 8 bits (1 byte), unsigned | 0 to 255 | 0 to 2^8 -1
Word | 16 bits (2 bytes), unsigned | 0 to 65,535 | 0 to 2^16 -1
Double-word | 32 bits (4 bytes), unsigned | 0 to 4,294,967,295 | 0 to 2^32 -1
Single-precision | 32 bits (4 bytes) | 8.43*10^-37 to 3.40*10^38 |
Double-precision | 64 bits (8 bytes) | 4.19*10^-307 to 1.79*10^308 |
Extended-precision | 80 bits (10 bytes) | 3.4*10^-4932 to 1.2*10^4932 |
Currency | 64 bits (8 bytes) | -9.22*10^14 to +9.22*10^14 |
Extended-currency | 64 bits (8 bytes) | -9.22*10^16 to +9.22*10^16 |
Variant | 128 bits (16 bytes) | {data-dependent} | {data-dependent}

Variable type-specifiers and keywords

Variable type

Type specifier

Element size

DEF type

Type keyword

N/A

4

N/A

PTR/POINTER

Integer

%

2

DEFINT

INTEGER

Long-integer

&

4

DEFLNG

LONG

Quad-integer

&&

8

DEFQUD

QUAD

Byte

?

1

DEFBYT

BYTE

Word

??

2

DEFWRD

WORD

Double-word

???

4

DEFDWD

DWORD

Single-precision

!

4

DEFSNG

SINGLE

Double-precision

#

8

DEFDBL

DOUBLE

Extended-precision

##

10

DEFEXT

EXT/EXTENDED

Currency

@

8

DEFCUR

CUR/CURRENCY

Extended-currency

@@

8

DEFCUX

CUX/CURRENCYX

String

$

4

DEFSTR

STRING

Fixed-length string

N/A

N/A

N/A

STRING * x

ASCIIZ string

N/A

N/A

N/A

ASCIIZ, ASCIZ

FIELD string

$

16

N/A

FIELD

Variant

N/A

16

N/A

VARIANT

GUID

N/A

16

N/A

GUID

OBJECT

N/A

4

N/A

DISPATCH


Byte (?)

Bytes are 8-bit (1 byte) unsigned integers ranging in value from 0 to 255 ( 0 to 2^8-1). The type-specifier character for a Byte is: ?.

Byte variables are identified by following the variable name with a question mark (i.e., var?), or by using the DEFBYT statement as described in the previous discussion of Integers. You can also declare Byte variables using the BYTE keyword with the DIM statement. For example:

DIM I AS BYTE

Byte variables are particularly useful for storing small, unsigned integer quantities like the number of days in a month. You should not use Byte variables in FOR/NEXT loops, as they are highly inefficient.

A PowerBASIC Byte variable is equivalent to a bool data type (in lowercase) used by most modern C compilers. A bool is a non-traditional 8-bit unsigned data type, whereas a BOOL data type (in capital letters) is equivalent to a Long-integer in PowerBASIC. Be aware that some older C compilers may freely interchange bool and BOOL keywords.

A Delphi byte is equivalent to a PowerBASIC Byte.

See Also

Double-word (???)

Integers (%)

Long integers (&)

Quad integers (&&)

Word (??)


Double-word (???)

Double-words are 32-bit (four byte) unsigned with a range of 0 to 4,294,967,295 ( 0 to 2^32-1). The type-specifier character for a Double-word is: ???.

Double-word variables are identified by following the variable name with three question marks (i.e., var???), or by using the DEFDWD statement as described in the previous discussion of Integers. You can also declare Double word variables using the DWORD keyword with the DIM statement. For example:

DIM I AS DWORD

As for Word values and Integers, Double-word values have a larger positive range than a Long-integer, and still require only four bytes. Double-word values are useful for indicating absolute memory addresses, such as may be used to store values.

A PowerBASIC Double-word is equivalent to a UINT32 in C/C++. In 32-bit C/C++ code, a UINT is also equivalent to a PowerBASIC Double-word variable. Note that 16-bit C/C++ code uses UINT to describe a 16-bit Word variable.

A C++ unsigned int and a Delphi longword are equivalent to a PowerBASIC Double-word.

See Also

Array Data Types

Bit Data Types

Constants and Literals

GUID Data Types

Object Data Types

User Defined Types

Unions

Variant Data Types


Integers (%)

To PowerBASIC, an Integer is a number with no decimal point (what mathematicians would call whole numbers) with a range of -32,768 to +32,767 ( -2^15 to 2^15 - 1). These values stem from the underlying 16-bit representation of an Integer: 32,768 is 2^15, and are therefore 2 bytes (16-bits) wide. The type-specifier character for Integer is: %.

Integers are identified by following the variable name with a percent sign (eg: var%), or by using the DEFINT statement. For example, if you use this declaration in your program code:

DEFINT I, J, K

…then all variables following this declaration that start with the letter I, J, or K will be an Integer by default. You can also declare an Integer variable using the INTEGER keyword with the DIM statement. For example:

DIM I AS INTEGER

A C/C++ short variable and a Delphi smallint are both equivalent to a PowerBASIC Integer.

See Also

Byte (?)

Double-word (???)

Long integers (&)

Quad integers (&&)

Word (??)


Long integers (&)

Like regular Integers, Long integers cannot contain decimal points. However, they span a much greater range, from -2,147,483,648 to +2,147,483,647 ( -2^31 to 2^31 - 1) yet occupy just 4 bytes (32-bits). The type-specifier character for a Long integer is: &.

Long integers are identified by following the variable name with an ampersand (i.e., var&) or by using the DEFLNG statement as described in the previous discussion of Integers. You can also declare Long-integer variables using the LONG keyword with the DIM statement. For example:

DIM I AS LONG

Long integers are the most efficient data type in PowerBASIC and should be used in all cases where speed is important and a greater numeric range is not required. (Using Byte and Integer variables in FOR/NEXT loops is actually slower than using a Long integer.)

A PowerBASIC Long-integer variable is equivalent to the BOOL data type (in capital letters) commonly used by C/C++ compilers. Note that a bool (lowercase) is a non-traditional data type, equivalent to a Byte in PowerBASIC. Be aware that some older C compilers may freely interchange bool and BOOL keywords.

A C/C++ int and a Delphi longint variable are also equivalent to a PowerBASIC Long integer.

See Also

Byte (?)

Double-word (???)

Integers (%)

Quad integers (&&)

Word (??)


Quad integers (&&)

Quad-integers are 64-bit (8 byte) signed integers (twice as many bits as Long integers) with a range of -9.22x10^18 to 9.22x10^18 ( -2^63 to 2^63 -1). The type-specifier character for a Quad integer is: &&.

Quad-integer variables are identified by following the variable name with two ampersands (i.e., var&&), or by using the DEFQUD statement as described in the previous discussion of Integers. You can also declare Quad-integer variables using the QUAD keyword with the DIM statement. For example:

DIM I AS QUAD

Although a Quad integer actually has 19 digits of precision, only 18 digits of accuracy can be "displayed" with STR$. A 19-digit value will be rounded to 18 digits in scientific notation when used with STR$. STR$ works with up to 16 significant digits by default, so the enhanced form of STR$ (eg: STR$(var,18)), must be used to generate the 17th and 18th digits of a Quad integer for display purposes.

A C/C++ LARGE_INTEGER and a Delphi int64 are both equivalent to a PowerBASIC Quad integer.

See Also

Byte (?)

Double-word (???)

Integers (%)

Long integers (&)

Word (??)


Word (??)

Words are 16-bit (two byte) unsigned integers with a range of 0 to 65535 ( 0 to 2^16-1). The type-specifier character for a Word is: ??.

Word variables are identified by following the variable name with two question marks (i.e., var??), or by using the DEFWRD statement as described in the previous discussion of Integers. You can also declare word variables using the WORD keyword with the DIM statement. For example:

DIM I AS WORD

Word values effectively extend the positive range for Integer, but still only require two bytes for storage.

A C/C++ UINT16 and a Delphi word are equivalent to a PowerBASIC Word.

See Also

Byte (?)

Double-word (???)

Integers (%)

Long integers (&)

Quad integers (&&)


Currency (@) and Extended-currency (@@)

Currency variables are 8 byte binary representations of numbers, which are considered to always have a fixed number of digits to the right of the decimal point. Currency numbers have a range of approximately -9.22*10^14 to +9.22*10^14, and Extended-currency have a range of -9.22*10^16 to +9.22*10^16.

The type-specifier character for Currency and Extended-currency floating-point is: @ and @@ respectively.

You can also use the DEFCUR or DEFCUX statement as described under Integers. They can also be declared using the CUR/CURRENCY or CUX/CURRENCYX keywords with the DIM statement. For example:

DIM I AS CUR

DIM J AS CURRENCYX

Currency variables (@) have up to 4 digits of precision after the decimal point, and are useful for prices and quantities where fractions of a cent are desired. Extended-currency variables (@@) have two digits of precision after the decimal point. They are optimized for financial calculations where fractions of a cent are not required.

The currency data types are especially useful for financial calculations, as they avoid the round-off errors associated with Single, Double, and Extended-precision numbers (which must be an exact power of two in order to be represented exactly). While many numbers can be represented exactly as a power of two, there are also many that cannot. For example, 1.10000002384185791 is the closest power of two to 1.1, in single precision.

So, when assigning numeric literal values to a Currency or Extended-currency variable, we recommend using a type specifier to ensure the value is given the intended precision. For example:

DIM x1 AS CUR

x1 = 1.0001@

DIM x2 AS CUX

x2 = 1.01@@

Internally, Currency and Extended-currency numbers are stored as Quad-integers with an implied decimal point (at 4 places for Currency, and at 2 places for Extended-currency). This approach ensures that all of the digits of the variables can be represented exactly.

Currency and Extended Currency perform a similar role as BCD variables in some BASIC dialects to ensure monetary values can be represented exactly; however, the internal storage of BCD variables and CUR/CUX differs substantially.

Delphi and Visual Basic both offer a currency data type that is identical to the PowerBASIC Currency variable.

See Also

Array Data Types

Bit Data Types

GUID Data Types

Object Data Types

User Defined Types

Unions

Variant Data Types


Double-precision floating-point (#)

Double-precision floating-point numbers are to Single-precision numbers what Long-integers are to Integers. They take twice as much space in memory (8 bytes versus 4 bytes), but have a greater range (+/- 4.19*10^-307 to 1.79*10^308) and a greater accuracy (15 to 16 digits of precision versus the 6 digits of Single-precision). A Double-precision, 5,000-element array requires 40,000 bytes. An Integer array with the same number of elements occupies only 10,000 bytes. The type-specifier character for a Double-precision floating-point is: #.

Double-precision variables are identified by following the variable name with a Number symbol (i.e., var#) or by using the DEFDBL statement as described in the previous discussion of Integers. You can also declare Double-precision variables using the DOUBLE keyword with the DIM statement. For example:

DIM I AS DOUBLE

C/C++, Delphi, and Visual Basic all offer a double data type that is identical to the PowerBASIC Double-precision variable.

See Also

Array Data Types

Bit Data Types

Constants and Literals

GUID Data Types

Object Data Types

User Defined Types

Unions

Variant Data Types


Extended-precision floating-point (##)

Extended-precision floating-point numbers are the basis of computation in PowerBASIC. The type-specifier character for an Extended-precision floating-point is: ##.

In PowerBASIC, arithmetic is optimized; it doesn't use Extended-precision unless it needs to. Extended-precision has been provided as a declarable , so that you can take advantage of its extra exponent range and precision if you wish.

Extended-precision values require 10 bytes of storage each. They have a range of approximately +/- 3.4*10^-4932 to 1.2*10^4932, and offer 18 digits of precision. All 18 digits can be "displayed" using the extended STR$ format (eg, STR$(var##,18)).

Extended-precision variables are identified by adding two Number symbols following a variable name (i.e., var##) or by using the DEFEXT statement.. You can also declare Extended-precision variables using the EXT or EXTENDED keywords with the DIM statement. For example:

DIM I AS EXT

DIM J AS EXTENDED

STR$ works with up to 16 significant digits (enough for a Double-precision value) by default. To get all 18 significant digits of an Extended-precision number in a , use the enhanced STR$ function to specify 18 digits:

x$ = STR$(a##,18)

See Also

Array Data Types

Bit Data Types

Constants and Literals

GUID Data Types

Object Data Types

User Defined Types

Unions

Variant Data Types


Single-precision floating-point (!)

Single-precision floating-point numbers (or more simply, Single-precision) may be the most versatile numeric type supported by PowerBASIC. Single-precision values can contain decimal points and have a range of +/- 8.43*10^-37 to 3.40*10^38. The type-specifier character for a Single-precision floating-point is: !.

Single-precision variables are identified by following the variable name with an exclamation point (i.e., var!) or by using the DEFSNG statement as described in the previous discussion of Integers. You can also declare Single-precision variables using the SINGLE keyword with the DIM statement. For example:

DIM I AS SINGLE

While Single-precision numbers can represent both enormous and microscopic values, they are limited to six digits of precision. In other words, Single-precision does a good job with figures like $451.21 and $6,411.92, but $671,421.22 cannot be represented exactly because it contains too many digits. Neither can 234.56789 or 0.00123456789. A Single-precision representation will come as close as it can in six digits: $671,421, or 234.568, or 0.00123457. Depending on your application, this rounding off can be a trivial or crippling deficiency. Like most modern compilers, PowerBASIC uses the IEEE standard for all floating-point arithmetic.

C/C++, Delphi, and Visual Basic all offer a single data type that is identical to the PowerBASIC Single-precision variable.

See Also

Currency (@) and Extended-currency (@@)

Double-precision floating-point (#)

Extended-precision floating-point (##)


ASCIIZ strings

You can think of ASCIIZ strings (or its synonym ASCIZ) as fixed-length strings where the last character is always a nul (CHR$(0) or $NUL) terminator. Like fixed-length strings, ASCIIZ strings contain character data of fixed length and any attempt to assign a string longer than the defined length will result in truncation.

If you assign a string (a string literal, or a dynamic or fixed-length string variable) to an ASCIIZ string that is shorter than the defined length, the string will not be padded on the right. Instead, the nul-terminator goes right after the last string character. The contents of the remainder of the string buffer are undetermined. Because an ASCIIZ string requires the nul-terminator byte, ASCIIZ strings are usually defined with a length of at least two bytes.

You declare ASCIIZ string variables using the ASCIIZ or ASCIZ keywords with the DIM statement. For example:

DIM MyStr1 AS ASCIIZ * 40

DIM MyStr2 AS ASCIZ * 40

This creates a 40 byte ASCIIZ string named MyStr. The number of characters you can store in an ASCIIZ string is always one less than the defined length of the string. The last character is used to hold the nul-terminator. Therefore, a 40 byte ASCIIZ string can hold up to 39 characters. If you need 40 usable bytes, DIM the string to have a 41 byte length. It is on this basis that ASCIIZ strings should be defined with a length of at least two bytes.

When assigning string data to an ASCIIZ string, the assignment will stop if an embedded $NUL byte is encountered. For example:

DIM a AS STRING

DIM b AS ASCIIZ * 10

a = CHR$("ABC", 0, "DEF")

b = a ' B will contain "ABC"

Unlike dynamic strings, the length of ASCIIZ strings is determined at compile-time, not run-time. In addition, unlike dynamic strings, ASCIIZ strings do not use handles. When you pass an ASCIIZ string to a routine, you are actually passing a pointer to the string's data.

The address of the contents of an ASCIIZ string can always be obtained with the VARPTR function. LOCAL ASCIIZ string memory is released when the associated Sub or Function ends. Subsequent calls to the Sub or Function will result in new storage locations for the ASCIIZ string data; however, the location of a LOCAL fixed-length string does not move until the string memory is released when the Sub/Function terminates.

LOCAL ASCIIZ strings are created on the stack frame, so LOCAL ASCIIZ strings will be limited to the amount of available stack space available. Typically this is less than 1 MB unless a larger stack frame has been allocated with #STACK metastatement. If larger ASCIIZ (or fixed-length) strings are required, it is advisable to make them STATIC or GLOBAL since those are not created within the stack frame.

The address of the contents of STATIC and GLOBAL ASCIIZ strings stays constant for the duration of the module. STATIC and GLOBAL Scalar (non-array) ASCIIZ strings may be up to 16,777,216 bytes each.

See Also

Dynamic (Variable-length) strings ($)

FIELD strings

Fixed-length strings

String expressions


Dynamic (Variable-length) strings ($)

Dynamic string variables contain character data of arbitrary length. Internally, each variable uses four bytes that contain a handle number, which is used to identify and locate information about a string. Dynamic strings can contain up to approximately 2 Gb (2^31) characters. The type-specifier character for a dynamic string is: $.

String variables are designated by following the variable name with a dollar sign ($) or the DEFSTR type definition. You can also declare dynamic string variables using the STRING keyword with the DIM statement. For example:

DIM MyStr AS STRING

PowerBASIC allocates strings using the Win32 OLE string engine. This allows you to pass strings from your program to DLLs, or API calls that support OLE strings. Note, however, that Visual Basic and Visual C++ store data in OLE strings using 16-bits per character (Unicode format) while PowerBASIC stores them in 8-bit format. In PowerBASIC, strings may contain either ASCII or ANSI string data.

The distinction between ASCII and ANSI only becomes important when using the strings for specific tasks. For example, when dealing with API calls, string data is usually interpreted as ANSI data by the API functions, whereas PowerBASIC statements such as UCASE$ treat the string data as ASCII.

Most standard DLLs designed to work with Visual Basic should still work with PowerBASIC, because VB converts OLE strings from Unicode to ANSI before passing them to a DLL, and PowerBASIC will accept and work with the ANSI string data.

The address of the contents of a non-empty string can be obtained with the STRPTR function. The address of the string handle can be obtained with VARPTR function. An empty (null) string may not return a valid STRPTR value.

Dynamic strings move in memory with each assignment statement: that is, STRPTR will return a different address when the content of the string is changed. However, the associated string handle obtained by VARPTR stays constant for the duration of the life (scope) of the string variable.

LOCAL dynamic string memory and handles are released when the associated Sub or Function ends. Subsequent calls to a Sub or Function will result in new storage locations for both the handle and the string data. The address of the handle of a STATIC or GLOBAL dynamic string stays constant for the duration of the module.

Dynamic strings and field strings cannot be part of UDT (User-Defined Type) or UNION structures.

In C/C++, a dynamic string is referred to as a BSTR data type.

See Also

ASCIIZ strings

FIELD strings

Fixed-length strings

String expressions


FIELD strings

A random-access file buffer is automatically created for use when GET or PUT statements are used without a target variable. In this case, the file data is read or written using this file buffer, and the buffer is accessed with one or more field strings.

If a field is defined by a single field (nSize) parameter, it represents the length of the field, with the start position implied by the preceding field within the statement. If two parameters are used, they represent the start (nStart) and end (nEnd) positions, indexed to one.

If a string value shorter than the declared size is assigned to a field string, it is padded with blank spaces and placed into the file buffer. There is no requirement to use LSET for assignment. When used with a file buffer, the field string is only valid when the nominated file is open. Once the file has been closed, field strings bound to the file buffer will be empty (zero length), rather than a string of the length defined in the FIELD statement. For example:

LOCAL fld1, fld2, fld3 AS FIELD

OPEN "test.dat" FOR RANDOM AS #1 LEN = 30

FIELD #1, 5 AS fld1, 10 AS fld2, 15 AS fld3

fld1 = "Bob" ' Stores "Bob "

CSET fld2 = "Zale" ' Stores " Zale "

RSET fld3 = "#1" ' Stores " #1"

? STR$(LEN(fld1)) ' Displays 5

? STR$(LEN(fld2)) ' Displays 10

? STR$(LEN(fld3)) ' Displays 15

CLOSE #1

? STR$(LEN(fld1)) ' Displays 0

A field variable bound to a dynamic string works very much like a , so the programmer must use care in field variable selection. For example, if you bind a GLOBAL FIELD variable to a LOCAL string variable, then attempt to reference the global string after the local is destroyed (i.e., released when the owning Sub/Function exits), a fatal exception error (GPF) is likely to occur. The same could happen after an array has been erased, or a REDIM is used to change the memory allocation. To avoid problems with scope, it is suggested that field variables be bound only with strings within the same scope (LOCAL, GLOBAL, etc.).

In addition, the dynamic string must contain data for the bound field strings to reference the data. For example:

LOCAL x$, sFirst AS FIELD, sSecond AS FIELD

FIELD x$, 3 AS sFirst, 3 AS sSecond

x$ = ""

? STR$(LEN(sFirst)) ' Displays 0 since x$ is empty

x$ = SPACE$(6) ' Allocate data to the string

sFirst = "111"

sSecond = "222"

? STR$(LEN(sFirst)) ' Displays 3 as x$ now contains data

Field strings and dynamic strings cannot be part of UDT (User-Defined Type) or UNION structures.

FIELD strings

Field strings are a special form of dynamic string, which have all the capabilities of a dynamic string, but may also represent a defined part of a random file buffer or a defined part of a dynamic string.

Field strings must always be declared using DIM, LOCAL, STATIC, GLOBAL, or THREADED. They may be used in the same manner as a dynamic string variable, or they can be bound to a file buffer for an open random-access file or a dynamic string using a corresponding FIELD statement. Each field string occupies sixteen bytes of memory, and requires slightly more general overhead than a regular dynamic string variable.

When used with a file

When used with a dynamic string

See Also

ASCIIZ strings

Dynamic (Variable-length) strings ($)

Fixed-length strings

String expressions


Fixed-length strings

As their name implies, fixed-length strings have a pre-defined length, and any attempt to assign a string longer than the defined length will result in truncation. If you assign a string (a string constant, or a dynamic or ASCIIZ string variable) to a fixed-length string that is shorter than the defined length, the string will be padded on the right with spaces. The major difference between dynamic strings and fixed-length strings is that once defined, the length of a fixed-length string cannot be changed. It is "fixed" for the duration of program execution.

You declare fixed-length string variables using the STRING * x format with the DIM statement. For example:

DIM MyStr AS STRING * 10

Although fixed-length strings were provided mainly to allow you to define string fields within a User-Defined Type or Union, they can also be used wherever other string values are valid.

Unlike dynamic strings, the length of fixed-length strings is determined at compile-time, not run-time. In addition, unlike dynamic (variable-length) strings, fixed-length strings do not use handles. When you pass a fixed-length string to a , you are actually passing a to the string's data.

A declaration of a fixed-length string or fixed-length string pointer must explicitly state the length of the variable, because the compiler must know it to allocate memory, and to pad the variable with spaces upon assignment.

The address of the contents of a fixed length string can always be obtained with the VARPTR function. LOCAL fixed-length string memory is released when the associated Sub or Function ends. Subsequent calls to the Sub or Function will result in new storage locations for the fixed-length string data; however, the location of a LOCAL fixed-length string does not move until the string memory is released when the Sub/Function terminates.

LOCAL fixed-length strings are created on the stack frame, so LOCAL fixed-length strings will be limited to the amount of available stack space available. Typically this is less than 1 MB unless a larger stack frame has been allocated with #STACK metastatement. If larger fixed-length (or ASCIIZ) strings are required, it is advisable to make them STATIC or GLOBAL since those are not created within the stack frame.

The address of the contents of STATIC and GLOBAL fixed-length strings stays constant for the duration of the module. STATIC and GLOBAL Scalar (non-array) fixed-length strings may be up to 16,777,216 bytes each.

See Also

ASCIIZ strings

Dynamic (Variable-length) strings ($)

FIELD strings

String expressions


String expressions

A string expression consists of string literals, , and string functions, optionally combined with the concatenation operators (+ or &). String expressions always produce strings as their result. Note that when the ampersand (&) is used as a string concatenation operator, it must be surrounded by white space, to differentiate it from the Long-integer type-specifier (i.e., LongVar&) and the number base prefix (i.e. &H0FF, &O77). Examples of string expressions include:

"Cats and dogs" ' string constant

firstname$ ' string variable

firstname$ + lastname$ ' string concatenation

a$ = "Cats " & "and " & "dogs" ' string concatenation

LEFT$(a$ + z$,7) ' string function

a$ + MID$("Cats and dogs",5,3)

RIGHT$(MID$(a$ + z$,1,6),3)

Note that fixed-length strings are always a fixed length (defined in the corresponding DIM statement), string concatenation involving these strings works differently than you might expect. For instance, the following program fragment:

DIM Greeting AS STRING * 40

Greeting = "hello"

Greeting = greeting + "there"

This appends (adds) the five-character string "there" to the 40-character fixed-length string ("hello", followed by 35 spaces), but the result is truncated to 40 characters (the predefined length of the string variable Greeting), which causes the newly appended string to be lost. One solution to this problem is to use the RTRIM$ function to remove the trailing spaces from "hello" before appending "there":

DIM Greeting AS STRING * 40

Greeting = "hello"

Greeting = RTRIM$(Greeting) + " there"

Variables of user-defined types may be used as string operands without any need to specify the individual UDT members:

TYPE MyType

ItemOne AS STRING * 10

ItemTwo AS STRING * 10

END TYPE

DIM SomeData AS MyType

SomeData.ItemOne = "hello"

SomeData.ItemTwo = "world!"

X$ = "Look at this!" + $CRLF + SomeData

See Also

ASCIIZ strings

Dynamic (Variable-length) strings ($)

FIELD strings

Fixed-length strings

String Operations


String Operations Commands

String Operations

The following functions manipulate and manage data:

ACODE$

Translate a Unicode string into an ANSI string

ARRAY ASSIGN

Assign a number of values to successive elements of an array

ARRAY DELETE

Delete a single item from a given array

ARRAY INSERT

Insert a single item into a given array

ARRAY SCAN

Scan all or part of an array for a given value

ARRAY SORT

Sort all or part of a given array

BIN$

Return a string with the binary (base 2) representation of a value

CHOOSE$

Return one of several values, based upon the value of an index

CHR$

Convert one or more ASCII codes into ASCII character(s)

CLSID$

Return a 16-byte (128-bit) GUID string containing a CLSID

COMM LINE

Receive a CR/LF terminated "line" of data from a serial port

COMM PRINT

Send a "line" of binary data through a serial port

COMM RECV

Receive binary data from a serial port

COMM SEND

Send a string of binary data through a serial port

COMMAND$

Return the command-line used to start the program

CSET

Center a string within the space of another string or UDT

CSET$

Return a string containing a centered (padded) string

CURDIR$

Return the current directory for a given drive

DATA

Declare an array of constants to be read by READ$

DATACOUNT

Return the total count of the number of local data items

DATE$

Set and retrieve the system date

DIM

Declare and dimension arrays, scalar variables, and pointers

DIR$

Return a filename that matches the given mask

DIR$ CLOSE

Force the release the operating system FindNext handle

ENVIRON

Modify the current program's environment table.

ENVIRON$

Retrieve strings from the operating system's environment table

ERASE

Deallocate array memory

ERROR$

Return a string containing the descriptive name of an error

EXTRACT$

Return up to the first occurrence of a specified character

FIELD

Bind a field string to a file buffer or dynamic string variable

FILENAME$

Return the file-system name of an open file

FORMAT$

Return a string containing formatted numeric data

FUNCNAME$

Return the name of the current Sub/Function

GET

Read a record from a random-access file

GET$

Read a string from a file opened in binary mode

GUID$

Return a 16-byte (128-bit) Globally Unique Identifier GUID

GUIDTXT$

Return a 38-byte human-readable GUID/UUID string

HEX$

Hexadecimal (base 16) string representation of an argument

IIF$

Return one of two values based upon a True/False evaluation

INKEY$

Read a keyboard or mouse event from the console input buffer

INPUT#

Load variables with data from a sequential file

INSTR

Search a string for the first occurrence of a character or string

JOIN$

Return a string consisting of all of the strings in a string array

LCASE$

Return a lowercase version of a string argument

LEFT$

Return the left-most n characters of a string

LEN

Return the logical length of a variable, UDT, or Union

LET

Assign a string or string array to a Variant

LINE INPUT

Read an entire line from the keyboard into a string variable

LINE INPUT#

Read line(s) from a sequential file into a string variable or array

LPRINT

Output text and data to a printer device

LPRINT$

Return the current printer device used for LPRINT operations

LSET

Left-align a string within the space of another string or UDT

LSET$

Return a string containing a left-justified (padded) string

LTRIM$

Return a string with leading characters or strings removed

MAX$

Return the argument with the largest (maximum) value

MCASE$

Return a mixed case version of a string argument

MID$

Return a portion of a string

MID$

Replace characters in a string with characters from another string

MIN$

Return the argument with the smallest (minimium) value

MKBYT$

Convert a Byte value into a binary encoded string

MKCUR$

Convert a Currency value into a binary encoded string

MKCUX$

Convert an Extended Currency value into a binary encoded string

MKD$

Convert a Double-precision value into a binary encoded string

MKDWD$

Convert a Double-word value into a binary encoded string

MKE$

Convert an Extended-precision value into a binary encoded string

MKI$

Convert a Integer value into a binary encoded string

MKL$

Convert a Long-integer value into a binary encoded string

MKQ$

Convert a Quad-integer value into a binary encoded string

MKS$

Convert a Single-precision value into a binary encoded string

MKWRD$

Convert a Word value into a binary encoded string

MKDIR

Create a subdirectory/folder (like the DOS MKDIR command)

NUL$

Return a string containing a specified number of $NUL characters

OCT$

Return a string that is a octal (base 8) representation of a value

PARSE

Parse a string and extract all delimited fields into an array

PARSE$

Return a delimited field from a string expression

PARSECOUNT

Return the count of delimited fields in a string expression

PEEK$

Return a sequence of bytes starting at a specific memory location

POKE$

Store a sequence of bytes starting at a specific memory location

PRINT

Write data to the current console text page (screen)

PRINT#

Write a complete array to a sequential file

PROGID$

Return the alphanumeric PROGID string (text) of a given CLSID

PUT

Write a record to a random-access file or variable to a binary file

PUT$

Write a string to a file opened in binary mode

READ$

Retrieve string data from a local DATA list

RE GEXPR

Scan a string for a matching "wildcard" or regular expression

REGREPL

Scan a "wildcard" match in a string with a new string

REMAIN$

Return all characters beyond occurrence of a specified character

REMOVE$

Return a copy of a string with characters or strings removed

REPEAT$

Return a string consisting of multiple copies of a specified string

REPLACE

Replace all occurrences of one string with another string

RESET

Clear a string, string array subscript, or an entire array

RETAIN$

Return a string with all non-specified characters removed

RIGHT$

Return the rightmost n characters of a string

RSET

Right justify a string into the space of a string variable or UDT

RSET$

Return a string containing a right-justified (padded) string

RTRIM$

Return a copy of a string with trailing characters/strings removed

SIZEOF

Return the total or physical length of any PowerBASIC variable

SPACE$

Return a string consisting of a specified number of spaces

STR$

Return the string representation of a number in printable form

STRDELETE$

Delete a specified number of characters from a string expression

STRING$

Return a string with multiple copies of the specified character

STRINSERT$

Insert a string at a specified position within another string

STRPTR

Return the address of the data held by a variable length string

STRREVERSE$

Reverse the contents of a string expression

SWAP

Exchange the values of two strings, , or pointer targets

SWITCH$

Return one item of a series based upon a True/False evaluation

TAB$

Return a string with TAB characters expanded with spaces

TALLY

Count the number of occurrences of specified characters/strings

TIME$

Read and/or set the system time

TRIM$

Return a string with leading and trailing characters removed

TYPE SET

Assign the value of a UDT or string expression to a UDT

UCASE$

Return an all-uppercase (capitalized) version of a string

UCODE$

Translate an ANSI string into a Unicode string

USING$

Format string/numeric expressions using a mask string

VAL

Return the equivalent of a string argument

VARIANT$

Return the dynamic string contained in a Variant variable

VARPTR

Return the 32-bit address of a string handle

VERIFY

Determine if each character of a string is in another string

WAITKEY$

Read a character or mouse event from the console input buffer


Array Data Types

It is often useful to treat a set of variables as a group. This lets you perform repetitive operations more easily. An array is a group of string or numeric data sharing the same variable name. The individual values that make up an array are called elements. An element of an array can be used in a statement or expression wherever you would use a regular string or numeric variable. In other words, each element of an array is itself a variable.

PowerBASIC provides several statements that perform operations on an array as a whole, allowing you to sort its contents, scan its contents for data that matches a certain condition, and insert data into or delete data from the existing structure.

You can think of an array as a row of boxes, numbered from zero to a predetermined number: four, in the example figure below. Each box holds a distinct value, which may or may not differ from the values in the other boxes. The boxes and their numbers are represented by parentheses surrounding a number; for example, item%(3) represents box number three of the array item%. Thus, if the value held within box number 3 is 1952, the statement total%=item%(3) would place the value 1952 into the variable total%.

Dimensioning a dynamic array with DIM or REDIM also clears each element, unless the PRESERVE option is present. Each element of each numeric array is set to zero; string arrays are set to the null string ("", length zero). Declaring the name and type of an array, as well as the number and organization of its elements, is performed by the DIM statement. For example:

DIM payments(55) AS CURRENCYX

…creates an array variable payments, consisting of 56 Extended-currency elements, numbered 0 through 55. Array payments and an Extended-currency variable also named payments are separate variables. If this is confusing, you'll understand why we suggested earlier that you use different variable names.

See Also

Subscripts

String arrays

Multidimensional arrays

Array storage requirements

Internal representations of arrays

Arrays within User-Defined Types

Array operations


Subscripts

Individual array elements are selected with subscripts or index numbers, which are Long-integer expressions within parentheses to the right of an array variable's name. For example, payments(3) and payments(44) are two of payments 56 elements. Normally, the first element of an array has a subscript value of zero, although this can be changed with the DIM statement. Some examples follow:

' This DIM statement declares a 56-element array

' with subscript bounds of 0 TO 55.

DIM payments1(55) AS CURRENCY

' This DIM statement declares a 56-element array

' with subscript bounds of 1 TO 56

DIM payments2(1 TO 56) AS CURRENCYX

You must DIM all arrays before you can use them. This is a different approach then that used by some BASIC dialects, which assume that an array contains 10 elements (0 to 9) if the array is not explicitly dimensioned.

PowerBASIC allows you to define a range of subscript values rather than just setting an upper limit. In addition to the TO syntax shown above, PowerBASIC supports the classic BASIC syntax that uses a colon (:) character to separate range values. Use the TO syntax in preference to the colon syntax, since the latter may not be supported in future versions of PowerBASIC. For example, the following two DIM statements are equivalent:

DIM fog(50:60) AS LONG ' Classic syntax

…or

DIM fog(50 TO 60) AS LONG ' Preferred syntax

…and both work to create an array fog, consisting of eleven Long-integer elements numbered 50 through 60.The statement:

DIM clouds(50 TO 60, 25 TO 45) AS LONG

creates the two-dimensional Long-integer array named clouds, containing 231 (11 * 21) elements. PowerBASIC's subscript range declaration capability allows you to model a programs data structures more closely to the problem at hand.

For example, consider a program tracking 19th-century birth statistics. This program's central data structure is a Long-Integer array of 100 elements that contain the number of babies born in each year of the last century. Ideally, you would create an array that used subscript values equal to the year in which the births occurred (for example, births(1851) represents how many babies came into the world in 1851), so that a code passage like:

DIM births(1899) AS LONG

FOR year& = 1800 TO 1899

INCR Total&, births(year&)

NEXT year&

would be as straightforward as possible. Unfortunately, DIM births(1899) AS LONG creates a 1900-element array (from 0 to 1899), of which the first 1800 are wasted. Traditionally, BASIC programmers have tackled this problem by declaring the array as:

DIM births&(99)

and by playing games with subscripts:

FOR year& = 1800 TO 1899

INCR Total&, births&(year&-1800)

NEXT year&

While this sort of thing works, it complicates things and slows programs down because suddenly there are 100 subtractions that weren't there before. It's better to declare a range, like this:

DIM births&(1800 TO 1899)' array births has subscripts

' ranging from 1800 to 1899

FOR year& = 1800 TO 1899

Total& = Total& + births&(year&)

NEXT year&

DIM birth1&(99) ' Array has 100 elements from 0 TO 99

DIM birth2&(1 TO 99) ' Array has 99 elements from 1 TO 99

DIM birth3&(3 TO 99) ' Array has 97 elements from 3 TO 99

See Also

Array Data Types

String arrays

Multidimensional arrays

Array storage requirements

Internal representations of arrays

Arrays within User-Defined Types

Array operations


String arrays

The elements of arrays hold strings instead of . Each string can be a different length. For example DIM words$(50) creates a sequence of 51 independent string variables:

DIM words$(50)

words$(0) = "Daniel likes cats." ' 18-character string

words$(1) = "" ' a null string

words$(2) = "Nicki is a sweet child." ' 23-character string

...

words$(50) = SPACE$(200) ' 200-character string

See Also

Array Data Types

Multidimensional arrays

Array storage requirements

Internal representations of arrays

Arrays within User-Defined Types

Array operations


Multidimensional arrays

Arrays can have one or more dimensions, up to a maximum of eight. A one-dimensional array such as payments is a simple list of values. A two-dimensional array represents a table of numbers with rows and columns of information. Some examples of multidimensional arrays are:

DIM one!(15) ' a one-dimensional list

DIM two!(15,20) ' a two-dimensional table

DIM three!(7,9,1) ' an 8 by 10 game board with room in the third

' dimension for 2 items: piece type and color

Arrays of four to eight dimensions are possible, but they become more difficult to conceptualize and keep straight. You can define:

DIM five%(5,5,10,20,3) ' a five-dimensional array

…but it's probably better to redesign this array into several smaller ones with fewer dimensions, or use an array of User-Defined Types.

See Also

Array Data Types

Subscripts

String arrays

Array storage requirements

Internal representations of arrays

Arrays within User-Defined Types

Array operations


Array storage requirements

A PowerBASIC array may contain up to 4,294,967,295 elements, and the data may occupy as much as all available memory (typically up to 2 Gb). However, all individual index numbers must fall within the range of a Long-integer variable (-2,147,483,648 to +2,147,483,647).

PowerBASIC stores array data in main memory for all array types (including LOCAL arrays). Therefore, there is no arbitrary array size limit imposed by the amount of free stack space, such as can be experienced with large LOCAL ASCIIZ, and Fixed-length string variables. The availability of main memory is the prime consideration (typically up to 2 Gb can be used). However, LOCAL arrays do require the storage of around 16 bytes on the stack for the array descriptor table.

See Also

Array Data Types

Subscripts

String arrays

Multidimensional arrays

Internal representations of arrays

Arrays within User-Defined Types

Array operations


Internal representations of arrays

PowerBASIC stores arrays in column-major order: Array(0,0) is first (lowest) in memory, then Array(1,0), then Array(2,0), and so on through all the rows of the array. After the rows are taken care of, the next column is stored.

While PowerBASIC supports lower boundary values that are non-zero, PowerBASIC generates the most efficient code if the lower boundary parameter is omitted (i.e., the array uses the default lower boundary of zero).

Array boundary values can be obtained at run-time via the LBOUND and UBOUND functions. Descriptive attributes of an array can be retrieved with the ARRAYATTR function. These attributes include such information as the and the number of dimensions, etc.

See Also

Array Data Types

Subscripts

String arrays

Multidimensional arrays

Array storage requirements

Arrays within User-Defined Types

Array operations


Arrays within User-Defined Types

In prior versions of this compiler, arrays could not be part of a UDT structure. However, we now support both one and two-dimensional arrays of variables that have a fixed-length (for each element) - this includes ASCIIZ strings, fixed-length strings, and all numeric variable classes. Individual arrays within a UDT may be up to 16 Megabytes each (although UDTs themselves are limited to 16 Megabytes).

Two-dimensional arrays within Types work exactly as do any other array in PowerBASIC, except that their dimensions are specified by positive numeric constant values, and are therefore not dynamically alterable. That is, the dimension sizes must be specified with numeric equates or numeric literal values, and these cannot be altered at run-time.

Like conventional arrays, the default lower array boundary is zero, but positive non-zero values may be used to specify a specific range of subscript index values for the array, separated from the upper array boundary subscript with the TO keyword. Additionally, both the lower and upper subscript index values must be zero or greater (ie, negative subscript values are not permitted). Examples of valid syntax follow:

TYPE MYTYPE

id AS INTEGER ' Scalar UDT member

Styles(6) AS DWORD ' 7 elements (0 TO 6)

Yrs(1980 TO 2010) AS LONG ' 31 elements

Team(100 TO 101) AS BYTE ' 2 elements

Rating(1 TO 10) AS DWORD ' 10 elements

X(1 TO 5, 0 TO 5) AS EXT ' 30 elements (5x6)

Y(4,3) AS QUAD ' 20 elements (5x4)

END TYPE

See Also

Array Data Types

Subscripts

String arrays

Multidimensional arrays

Array storage requirements

Internal representations of arrays

Array operations


Array Operations

The following functions can be used to manipulate and manage arrays:

#DEBUG ERROR

Control generation of error checking code

#DIM

Specify if variables must be declared before use

ARRAY ASSIGN

Assign a number of values to successive elements of an array

ARRAY DELETE

Delete a single item from a given array

ARRAY INSERT

Insert a single item into a given array

ARRAY SCAN

Scan all or part of an array for a given value

ARRAY SORT

Sort all or part of a given array

ARRAYATTR

Return descriptive attributes of a given array

BIT CALC

Set or reset a bit in an implied bit-array

BIT

Return the value of a particular bit in an implied bit-array

BIT

Manipulate individual bits of an implied bit-array

DATA

Declare an array of constants to be read by READ$

DATACOUNT

Return the total count of the number of local data items

DIM

Declare and dimension arrays, scalar variables, and

ERASE

Deallocate array memory

FILESCAN

Rapidly scan an open file, before loading into an array with GET

GET

Read a complete array from a binary file

JOIN$

Return a consisting of all of the strings in a string array

LBOUND

Return the lowest subscript of an array's specific dimension

LET

Assign a Variant to an array or an array to a Variant

LINE INPUT#

Read line(s) from a sequential file into a string variable or array

MAT

Matrix calculations on numeric arrays

PARSE

Parse a string and extract all delimited fields into an array

PRINT#

Write a complete array to a sequential file

PUT

Write a complete array to a binary file

READ$

Retrieve string data from a local DATA list

REDIM

Declare dynamic arrays, allocate, reallocate, deallocate memory

RESET

Set an array subscript or an entire array to zero or null/empty

UBOUND

Return the highest subscript of an array's specific dimension


User-Defined Types (UDTs)

Arrays are useful when you need to treat a set of similar variables as a unit. For instance, ten test scores or ten student names. But what if you need to store several unrelated data types and be able to treat them as a unit? That is where User-Defined Types come in. When you define a User-Defined Type (UDT), you are actually defining a template for a new variable Type.

Once created, you can define as many variables of your new Type as you please. Moreover, since User-Defined Types can be associated with a random file's buffer, this provides you with a whole new way to access your random files.

PowerBASIC's User-Defined Type is similar to a C struct or Pascal record. The elements of a User-Defined Type may include any of PowerBASIC's data types, with the exception of dynamic (variable-length) strings, field strings, and arrays of dynamic strings.

To get an idea of the power of the User-Defined Type, imagine you are a teacher who needs a program to keep track of student grades. Since your school is on a very tight budget (and what schools aren't these days?), you decide to write the program yourself in PowerBASIC. For each student in the class you need to track the following information:

  • The student's name

  • A student number

  • A mailing address

  • The name and phone of the person to contact in case of an emergency

  • The relationship of the contact person to the student

Currently these records are being kept in a small file box. The information about each student is contained on a single file card. How do you transfer this information to the computer? Simple. Define a Student Record Type that will contain all the information about a single student.

The variables you create as User-Defined Types are often called records or record variables, since each variable of that Type contains one record, or one set of related information. The individual elements are referred to as fields or members. In the example above, each set of student information is a record, and each piece of information within that record (the last name for example) is a field.

See Also

Accessing the fields of a User-Defined Type

Nesting User-Defined Types

Arrays within User-Defined Types

Using arrays of User-Defined Types

Using User-Defined Types with procedures and functions

Storage requirements and restrictions

Unions


Defining User-Defined Types

The definition of a User-Defined Type begins with the reserved word TYPE and ends with the keywords END TYPE. In between, you define the names and of the member elements (fields) that are to be part of the new Type. For example:

TYPE StudentRecord

LastName AS STRING * 20 ' A 20-character string

FirstName AS STRING * 15 ' A 15-character string

IDnum AS LONG ' Student ID, a Long-integer

Contact AS STRING * 30 ' Emergency contact person

ContactPhone AS STRING * 14 ' Their phone number

ContactRel AS STRING * 8 ' Relationship to student.

AverageGrade AS SINGLE ' Single-precision % grade

END TYPE

Remember that the definition of a User-Defined Type does not set aside memory for storing data of that Type. Rather, it defines a template for the new Type StudentRecord. Then when the compiler encounters a statement declaring (or creating) a variable of the new Type, it will "know" how many bytes of storage to set aside for the variable. In order to use this new Type, you must declare variables of that Type with the DIM statement:

DIM Student AS StudentRecord

See Also

User-Defined Types (UDTs)

Accessing the fields of a User-Defined Type

Nesting User-Defined Types

Arrays within User-Defined Types

Using arrays of User-Defined Types

Using User-Defined Types with procedures and functions

Storage requirements and restrictions

Unions


Accessing the fields of a User-Defined Type

To work with the individual fields within a record variable, separate the field name from the variable name with a period. Here are some examples using the Student variable in the above DIM statement:

Last$ = Student.LastName

Message$ = "Id number is: " + STR$(Student.IdNum)

Student.FirstName = "Bob"

Student.LastName = "Smith"

Fullname$ = Student.LastName + " " + Student.FirstName

Fullname$ = RTRIM$(Student.LastName) + ", " + RTRIM$(Student.FirstName)

Note that the last two statements above produce slightly differing results. The former produces a string that contains the text plus any $SPC (space) characters that pad the text in each of the Student.LastName and Student.FirstName members. Comparatively, the latter statement returns a string with these padding characters removed. In many cases, it can be easier to use ASCIIZ string members to alleviate the need to frequently trim such fixed-length strings, but allowance must be made for the additional $NUL terminator byte required by ASCIIZ strings.

See Also

User-Defined Types (UDTs)

Defining User-Defined Types

Accessing the fields of a User-Defined Type

Nesting User-Defined Types

Arrays within User-Defined Types

Using arrays of User-Defined Types

Using User-Defined Types with procedures and functions

Storage requirements and restrictions

Unions


Nesting User-Defined Types

The fields within a User-Defined Type can be made up of other User-Defined Types. Just like a set of Chinese boxes, with each box containing a smaller box, you can nest one User-Defined Type within another. The result is that you create data structures that have a hierarchy similar to the directory tree structure of your hard drive.

Instead of storing the student names as two separate fields, we could instead define a Type called NameRec as follows:

TYPE NameRec

Last AS STRING * 20

First AS STRING * 15

Initial AS STRING * 1

END TYPE

Then, when we define our Student Record Type, we can define the field containing the individual student's name as NameRec:

TYPE StudentRecord

FullName AS NameRec

IdNum AS LONG

Contact AS NameRec

ContactPhone AS STRING * 14

ContactRel AS STRING * 8

AverageGrade AS SINGLE

END TYPE

You could, of course carry this idea a step further, and define other components of the student record as nested records. For instance, a ContactRecord or even a PhoneRec but we'll leave that refinement up to you. To access the fields of a nested record, simply extend the dot notation. Just as the backslash (\) is used to separate the individual subdirectory names in a path (i.e., C:\PROJECTS\PROGRAM), the period is used within record variable names to separate the member elements from the base Type. For instance:

StudentRecord.FullName

refers to the FullName field (which happens to be of Type NameRec) within Student Record, and:

StudentRecord.FullName.First

refers to the sub-field First within the FullName field.

You can nest User-Defined Types as deeply as you want to, as long as the entire name used to refer to a field is within the maximum identifier length of 255 characters. In practical terms however, you probably would not want to carry nesting beyond two or, at most, three levels. Beyond that, it becomes clumsy, difficult to remember, and you are more likely to make typing errors. Note that User-Defined Types cannot contain circular references - for example, a UDT called StudentRecord cannot contain a field of Type StudentRecord.

See Also

User-Defined Types (UDTs)

Defining User-Defined Types

Accessing the fields of a User-Defined Type

Arrays within User-Defined Types

Using arrays of User-Defined Types

Using User-Defined Types with procedures and functions

Storage requirements and restrictions

Unions


Using arrays of User-Defined Types

You can create arrays of User-Defined Types just as you can create arrays of or or any of PowerBASIC's other data types. For example:

DIM Class(1 TO 30) AS StudentRecord

To access the individual elements of the Class array, you use subscript index values just as you do with any other array. The third student record is Class(3), for instance. The period separator and the field name follows the array subscript:

Class(3).FullName.First

This would access the first name of the third student in the class array. Think of it this way: the array is made up of elements of the Type Student Record, so the subscript belongs with the name of the variable as a whole.

You can create multidimensional arrays of User-Defined Types just as you can with any other PowerBASIC data type. The limit on the number of elements and dimensions in such arrays is governed by the same rules as well: The limits are defined by the amount of data storage required for each element. Additionally, arrays within structures must contain a static subscript list, defined at compile-time. Therefore, arrays within structures cannot be redimensioned at run-time.

See Also

User-Defined Types (UDTs)

Defining User-Defined Types

Accessing the fields of a User-Defined Type

Nesting User-Defined Types

Arrays within User-Defined Types

Using User-Defined Types with procedures and functions

Storage requirements and restrictions

Unions


Using User-Defined Types with procedures and functions

Using User-Defined Types with subroutines and functions

Subroutines and functions can process User-Defined Types as well as any other data type. This topic covers the following topics:

· Passing fields as arguments

· Passing records as arguments

· Passing record arrays as arguments

Passing fields as arguments

Members in User-Defined Types that are one of the built-in PowerBASIC types (INTEGER, WORD, STRING, and so on) can be passed to procedures and functions as if they were simple variables. For example, given the User-Defined Type PatientRecord, as follows:

TYPE PatientRecord

FullName AS STRING * 32

AmountDue AS DOUBLE

IdNum AS LONG

END TYPE

DIM Patient AS PatientRecord

…you could use a procedure PrintStatement:

SUB PrintStatement(Id AS LONG, AmountPastDue AS DOUBLE)

' access Id and AmountPastDue

...

END SUB

…like this:

CALL PrintStatement(Patient.IdNum, Patient.AmountDue)

Passing records as arguments

You can also write your procedures and functions to accept arguments of User-Defined Types. This is especially useful if you want to pass many arguments; rather than have a long argument list, you can pass a single User-Defined Type. For example, given the PatientRecord User-Defined Type discussed in the previous section, you could write your PrintStatement procedure as follows:

SUB PrintStatement(Patient AS PatientRecord)

' access Patient.IdNum and Patient.AmountDue

...

END SUB

You'd call PrintStatement like this:

CALL PrintStatement(Patient)

Passing record arrays as arguments

Procedures and functions can accept arrays of records as easily as they can accept arrays of other Types. For example, if you had an array of PatientRecords, each containing a patient record with an amount due, you could write a function that returns the total amount due for all the patient records in the array:

FUNCTION TotalAmountDue(Patients() AS PatientRecord)

DIM total AS DOUBLE

RESET total

FOR ix = LBOUND(Patients) TO UBOUND(Patients)

total = total + Patients(ix).AmountDue

NEXT

TotalAmountDue = total

END FUNCTION

You might call the function like this:

DIM Patients(1 TO 100) AS PatientRecord

...

x$ = "Total amount due:" + STR$(TotalAmountDue(Patients()))

See Also

User-Defined Types (UDTs)

Storage requirements and restrictions

Unions


Storage requirements and restrictions

You can determine the amount of storage required for a variable of a User-Defined Type using the LEN function. To determine the requirements for a student record, for example, use:

RecordSize = LEN(Student)

The address of a record variable, as returned by the VARPTR function, is the address in memory of the first byte of data in the record. You can also obtain the starting address of the fields within the record by passing the full name of the field (Student.IdNum, for example) to the VARPTR function.

A single UDT structure is limited to 16 MB (16,777,216 bytes). Locally dimensioned UDT structures are limited to the amount of free stack space available, typically less than 1 MB. If larger UDT structures are required, use a STATIC or GLOBAL declaration instead (since these are not stored on the stack). The same rules apply to Unions (and LOCAL fixed-length and ASCIIZ strings).

Note that the statements cannot be directly used on arrays within UDTs. However, you can use DIM..AT to define an array (of the same data type) at the address of the UDT array, and employ ARRAY statements on that array. The ARRAY statements can be used on arrays of UDT structures. An individual array within a UDT may occupy as much as the full 16 MB UDT size limit.

See Also

User-Defined Types (UDTs)

Unions


Unions

If you have ever programmed in Pascal or C, you may be familiar with the concept of a Union. A Union is similar in some ways to a User-Defined Type. Both have data fields that can be made up of any of PowerBASIC's data types, including records and other Unions, and except for the UNION keyword, they are defined the same way. The major difference between User-Defined Types and Unions, is that each field within a Union occupies the same memory location as all the others.

While the concept may appear abstract, Unions provide an avenue to freely convert data from one format to another, simply by writing the data into the Union as one data format, and reading the data back as another. Combining the versatility of a UDT with the flexibility of a Union can extend this functionality dramatically, such as splitting data into its component parts.

For example, the following definition would create a Union called WordFld and a WordFld variable called MyVar:

TYPE HiLo

Hi AS BYTE

Lo AS BYTE

END TYPE

UNION WordFld

Whole AS WORD

Part AS HiLo

END UNION

DIM MyVar AS WordFld

MyVar.Whole = &HBC1F 'assign a value to the entire word

a$ = HEX$(MyVar.Part.Hi) 'returns Hi byte of the word

b$ = HEX$(MyVar.Part.Lo) 'returns Lo byte of the word

When you access the field MyVar.Whole, you are reading the entire contents of the Union as a word. On the other hand, when you refer to MyVar.Part.Hi, you are referring to the high byte of MyVar.

See Also

User-Defined Types (UDTs)

Union Storage requirements and restrictions


Union Storage requirements and restrictions

Storage requirements and restrictions

A single Union structure is limited to 16 MB (16,777,216 bytes). Locally dimensioned Union structures are limited to the amount of free stack space available, typically less than 1 MB. If larger UDT structures are required, use a STATIC or GLOBAL declaration instead, since these are not created on the stack. The same rules apply to User-Defined Types (and LOCAL fixed-length and ASCIIZ strings). An individual array within a Union may occupy as much as the full 16 MB Union size limit.

See Also

User-Defined Types (UDTs)

Unions


Pointers (@)

A pointer is a variable that holds the 32-bit (4 byte) address of code or data located elsewhere in memory. It is called a pointer because it literally points to that location. The location pointed to is known as the target of the pointer.

Pointers represent a powerful addition to the BASIC programmer's arsenal. The address is defined at run-time, so your program can reference any memory location as if it were a standard variable. When a pointer is used to access a memory location, it is called "indirect addressing".

Pointers are declared using the DIM statement, and the type of the target must be specified. The keywords PTR and POINTER are synonymous.

DIM i AS INTEGER PTR 'declares i as a pointer to an Integer

or:

DIM i AS INTEGER POINTER

The above example declares i as an Integer pointer. Before it can be used, i must be initialized with an actual address of a variable (easily done with the VARPTR function; or STRPTR for ). When you assign a value to a pointer variable, you are giving it an address to use later when you wish to reference the actual target. A pointer's name alone references the pointer variable. A pointer's name with an at sign (@) prefix, references the pointer's target:

DIM Ptr1 AS BYTE PTR ' declares Ptr1 as a byte pointer

DIM Ptr2 AS BYTE PTR ' declares Ptr2 as a byte pointer

DIM Byte1 AS BYTE ' Declares Byte1 as a byte variable

DIM Byte2 AS BYTE ' Declares Byte2 as a byte variable

Ptr1 = VARPTR(Byte1) ' Ptr1 points to Byte1

@Ptr1 = 36 ' Sets Byte1 to the value 36

Ptr2 = VARPTR(Byte2) ' Ptr2 points to Byte2

@Ptr2 = @Ptr1 + 4 ' Sets Byte2 to 40 (36 + 4)

In summary, when you reference a pointer variable without an at-sign, you are referencing the 32-bit address contained in it. When you precede the name with an at-sign, you are referencing the target data located at the address "pointed to" by the pointer.

By assigning the address of another pointer to a pointer, we can set up another level of indirection. Pointers to pointers are useful when setting up linked lists in memory. You can then access the target by adding a second at-sign in front of the pointer's name:

DIM y AS STRING POINTER

DIM z AS STRING POINTER

DIM TmpStr AS STRING

y = VARPTR(TmpStr) ' y points to TmpStr

z = VARPTR(y) ' z points to y

@y = "A" ' put an "A" in TmpStr

@@z = "B" ' overwrite it with a "B"

Display @y ' display the target value of y

PowerBASIC supports up to 200 levels of indirection. For each level, you add another preceding at-sign to the pointer name. You can only use the (@) prefix with pointer variables.

A pointer with a value of zero (0) is considered a null-pointer by PowerBASIC. Windows will generate a General Protection Fault (GPF) if you attempt to access data at an invalid pointer address. See the section on assembler programming for more information.

The true power of pointers resides in their speed and flexibility. Traditionally, to access memory, a BASIC programmer had to use combinations of PEEK and POKE. This allowed the programmer to address memory as bytes. If the target data took any other form, conversion was necessary. Pointers allow you to address the target data in any fashion you desire, even as a user-defined structure. Moreover, because the setup of calling PEEK and POKE is no longer necessary, access is much faster.

Let's say that we want to scan all the characters in a buffer, replacing all upper case "A"s with lower case "a"s. The code might look something like this:

SUB Lower(zStr AS STRING)

DIM s AS BYTE PTR, ix AS INTEGER

s = STRPTR(zStr) ' Access the dynamic string directly

FOR ix = 1 TO LEN(zStr)

IF @s = 65 THEN @s = 97 ' "A" -> "a"

INCR s

NEXT

END SUB

When using a pointer to a structure, the prefix is placed before the structure name when you wish to access an element of the structure. The structure name by itself refers to its address. This distinction is extremely important when treating structures as a whole. The following example shows two ways of doing a simple bubble sort of an array of User-Defined Types. The first uses conventional BASIC methods, the second uses pointers to illustrate their speed and efficiency.

'-- Example 1 --------------

#COMPILE EXE

#DIM ALL

TYPE NameRec

Last AS STRING * 20 ' Last name

First AS STRING * 20 ' First name

END TYPE

FUNCTION PBMAIN () AS LONG

DIM Rec(1 TO 10) AS NameRec

DIM RP AS NameRec POINTER

DIM ix AS LONG, ij AS LONG

DIM hFile AS DWORD

'-- Put some data in the records --

FOR ix = 1 TO 10

Rec(ix).First = CHOOSE$(ix,"Jacob","Michael","Joshua","Matthew","Ethan", _

"Emily","Emma","Madison","Abigail","Olivia")

Rec(ix).Last = CHOOSE$(ix,"SMITH","JOHNSON","WILLIAMS","JONES","BROWN",

_

"DAVIS","MILLER","WILSON","MOORE","TAYLOR")

NEXT ix

'-- Sort UDT array in ascending order using a bubble sort

'-- ARRAY SORT Rec(),FROM 1 TO 20,ASCEND will do this as well

FOR ix = 9 TO 1 STEP -1

FOR ij = 1 TO ix

IF Rec(ij-1).Last > Rec(ij).Last THEN

SWAP Rec(ij-1), rec(ij)

END IF

NEXT ij

NEXT ix

#IF %DEF(%PB_CC32)

FOR ix = 1 TO 10

PRINT TRIM$(Rec(ix).Last)+ ", " +TRIM$(Rec(ix).First)

NEXT ix

PRINT

PRINT "Press any key to quit ... "

WAITKEY$

#ELSE

DIM msg AS STRING

FOR ix = 1 TO 10

msg = msg + TRIM$(Rec(ix).Last)+ ", " +TRIM$(Rec(ix).First) + $CRLF

MSGBOX msg

NEXT ix

#ENDIF

END FUNCTION

'-- Example 2 --------------

' The difference between example 1 and this example is

' that we're manipulating pointers (4 bytes) instead

' of whole records (40 bytes).

#COMPILE EXE

#DIM ALL

TYPE NameRec

Last AS STRING * 20 ' Last name

First AS STRING * 20 ' First name

END TYPE

FUNCTION PBMAIN () AS LONG

DIM Rec(1 TO 10) AS NameRec

DIM RP AS NameRec POINTER

DIM ix AS LONG, ij AS LONG

DIM hFile AS DWORD

'-- Put some data in the records --

FOR ix = 1 TO 10

Rec(ix).First = CHOOSE$(ix,"Jacob","Michael","Joshua","Matthew","Ethan", _

"Emily","Emma","Madison","Abigail","Olivia")

Rec(ix).Last = CHOOSE$(ix,"SMITH","JOHNSON","WILLIAMS","JONES","BROWN",

_

"DAVIS","MILLER","WILSON","MOORE","TAYLOR")

NEXT ix

'-- Sort UDT array in ascending order using a bubble sort with pointers

'-- note a bubble sort is not recommended for large collections

'-- and note ARRAY SORT Rec(),FROM 1 TO 20,ASCEND will do this as well

'-- so this is only to show pointers to UDT arrays in action!

RP = VARPTR(Rec(1))

FOR ix = 9 TO 1 STEP -1

FOR ij = 1 TO ix

'note pointers to array elements use zero based subscripts in brackets!

IF @RP[ij-1].Last > @RP[ij].Last THEN

SWAP @RP[ij-1], @RP[ij]

END IF

NEXT ij

NEXT ix

#IF %DEF(%PB_CC32)

FOR ix = 1 TO 10

PRINT TRIM$(Rec(ix).Last)+ ", " +TRIM$(Rec(ix).First)

NEXT ix

PRINT

PRINT "Press any key to quit ... "

WAITKEY$

#ELSE

DIM msg AS STRING

FOR ix = 1 TO 10

msg = msg + TRIM$(Rec(ix).Last)+ ", " +TRIM$(Rec(ix).First) + $CRLF

MSGBOX msg

NEXT ix

#ENDIF

END FUNCTION

If you declare a member of a structure as a pointer, the @ prefix is used with the member name, not the structure name. The previous example could be improved by adding a couple of pointers to the structure to point to the previous and next record, respectively. This lets you allocate memory for a record only when needed, instead of pre-allocating a fixed-size array of records. The modified structure would look something like this:

TYPE NameRec

Last AS STRING * 20 ' Last name

First AS STRING * 20 ' First name

Nxt AS NameRec PTR ' Pointer to next record

Prv AS NameRec PTR ' Pointer to previous record

END TYPE

DIM Rec AS NameRec

The pointer members are then accessed like this:

Rec.@Nxt ' next record

Rec.@Prv ' previous record

Putting the @ prefix in front of the structure name (i.e., @Rec) would cause a compile-time error, as Rec itself is not a pointer.

When calculating the length of the Type, all pointers are internally stored as Double-word (DWORD) variables, so NameRec is 48 bytes long (20 + 20 + 4 + 4). If you need to know the length of a Type, it is easier to let PowerBASIC calculate it for you using the LEN function than doing it yourself:

length = LEN(structure)

See Also

Pointers to ASCIIZ and fixed-length strings

Pointers to arrays

Pointers to arrays with dual indexes


Pointers to ASCIIZ and fixed-length strings

A declaration of an ASCIIZ or fixed-length string must explicitly state the maximum length of the string, in order for the compiler to allocate memory accordingly. When declaring pointers to fixed-length strings, you may also state the maximum length of the string. This will allow INCR and DECR to move the pointer to the next or previous string, respectively. If you do not supply the length for a fixed-length string pointer, INCR and DECR will move the pointer by one byte.

However, with an ASCIIZ string pointer, the length limit may be explicitly stated, or it may be left as an ambiguous value, by skipping the length clause entirely. For example, the following lines are valid:

DIM x AS ASCIIZ PTR * 41

DIM y AS ASCIIZ PTR * 2

DIM z AS ASCIIZ PTR

This rule applies to scalar pointers, arrays of pointers, pointers as function parameters, and pointers as members of a User-Defined Type or Union. If the optional length limit is specified, PowerBASIC will always truncate a string assignment to fit correctly in the memory allocated to the variable.

If the length is ambiguous, it becomes the programmer's responsibility to ensure the target buffer is not overflowed leading to memory corruption or General Protection Faults (GPF). Use caution in this case.

See Also

Pointers (@)

Pointers to arrays

Pointers to arrays with dual indexes


Pointers to arrays

In order to work with arrays created by other languages, such as VB arrays, PowerBASIC supports an extension of the pointer syntax, called "Pointer Indexing". As noted above, a pointer allows you access a data element at specific address in memory. An index pointer allows you to access data elements beyond the memory address in the base pointer. Consider an array with 6 elements:

DIM x%(0 TO 5)

The address of the first element, x%(0), is the base with the remaining elements stored in memory, one after the other. To access the array using an index pointer, you simply assign the address of the first element to your base pointer:

DIM xPtr AS INTEGER POINTER

xPtr = VARPTR(x%(0))

@xPtr[0] = 0 ' same as x%(0)

@xPtr[1] = 1 ' same as x%(1)

@xPtr[2] = 2 ' same as x%(2)

@xPtr[3] = 3 ' same as x%(3)

@xPtr[4] = 4 ' same as x%(4)

@xPtr[5] = 5 ' same as x%(5)

Note the syntax used to access the elements of the array. It consists of the pointer's name, with the @ prefix, and followed by the array index in square brackets. The number used inside of the brackets is a multiplier. The number inside the brackets is multiplied by the size of the target data (a two byte Integer in this case) to calculate the target address.

The primary differences between arrays and index pointers are than index pointers do not allocate any memory of their own - they use memory which has already been allocated elsewhere. Their lower bound is always zero. For example, you can dimension your six-element array from 1990 to 1995. However, to access the array data using an index pointer, you will still need to use 0 through 5:

x%(1990 TO 1995)

DIM xPtr AS INTEGER PTR

xPtr = VARPTR(x%(1990))

@xPtr[0] = 0 ' same as x%(1990)

@xPtr[1] = 1 ' same as x%(1991)

@xPtr[2] = 2 ' same as x%(1992)

@xPtr[3] = 3 ' same as x%(1993)

@xPtr[4] = 4 ' same as x%(1994)

@xPtr[5] = 5 ' same as x%(1995)

Consider the following VB code:

Sub Sum_Click()

ReDim PriceData!(1 TO TotalElements%)

Call FillSumArray(PriceData!())

Total! = GetSum(PriceData!(1), TotalElements%)

End Sub

When the "Sum" button is pressed, a dynamic array is created and filled. FillSumArray() is VB code to read the price data from a database file and place it into the array. GetSum() is PowerBASIC code to add up all of the prices and return the total, since PowerBASIC handles calculations faster than VB does.

FUNCTION GetSum!(Price!, BYVAL TotalElements%) EXPORT

DIM PriceData AS SINGLE PTR

DIM Total!

DIM k%

PriceData = VARPTR(Price!)

FOR k% = 0 TO TotalElements% - 1

Total! = Total! + @PriceData[k%]

NEXT

GetSum! = Total!

END FUNCTION

In the above example, GetSum! takes the Visual Basic array, adds up all the values, and returns the total as a result. Since a pointer is a memory address, we need the memory address of the first element in the array. In VB, you can pass the memory address of a variable by passing it "by reference", or . This tells Visual Basic not to pass the value of a variable to a Sub or Function, but to pass the address in memory where the variable is located. This is handled through the DECLARE statement in Visual Basic.

DECLARE FUNCTION GetSum! LIB "SUMS.DLL" (Prices!, BYVAL Elements%)

By not using the keyword before the variable Prices!, we've told Visual Basic to pass a memory address to the variable. You'll notice in the DECLARE statement that the variable Prices! does not include any parentheses to indicate that it is an array. If we were to change it to Prices!(), VB would pass a handle to an array descriptor, not an address to the array data. The PowerBASIC code also needs to know how many elements there are in the array, so that is passed as the second parameter.

Since only the first element of the array is passed to GetSum!, we'll need to use a pointer to access the remainder of the elements.

DIM PriceData AS SINGLE PTR

Remember that all pointers are initialized to null (zero). To access the array, we need to assign the memory address for the element passed. VARPTR is used to get the address of the passed element.

PriceData = VARPTR(Price!)

An indexed pointer can then be used to access all of the elements in the array. The VB array was dimensioned from 1 to TotalElements; however indexed pointers in PowerBASIC all start with a subscript of zero. So to reconcile the difference, we subtract the lower bound (1) from TotalElements in our FOR/NEXT loop. A DIM statement is not required to access an array using this method.

FOR k% = 0 TO Elems% - 1

Total! = Total! + @PriceData[k%]

NEXT

It is also possible to use indexed-pointers with dynamic string arrays. For example:

DIM Arr1(1 TO 3) AS STRING

DIM pArr1 AS STRING POINTER

Arr1(1) = "a1"

Arr1(2) = "a2"

Arr1(3) = "a3"

PArr1 = VARPTR(Arr1(1)) ' The 1st array element

DisplayText @pArr1[2] ' This references Arr1(3)

Indexed pointers make it easy to manipulate arrays created by other languages such as VB, Delphi, C/C++, etc.

See Also

Pointers (@)

Pointers to ASCIIZ and fixed-length strings

Pointers to arrays with dual indexes


Pointers to arrays with dual indexes

Indexed pointers with dual indexes require an "OF limit" clause on both indexes. While simple arrays (arrays with one index) store data sequentially, dual indexes interleave each row of data. The OF clause is used by the compiler to calculate the size of each row and column. limit is the upper bound of the index (zero-based):

DIM DataPtr AS INTEGER PTR

DIM z%(0 TO 8, 0 TO 3)

DataPtr = VARPTR(z%(0, 0))

FOR y = 0 TO 3

FOR x = 0 TO 8

Value% = @DataPtr[x OF 8, y OF 3]

NEXT x

NEXT y

The following example uses a lower bound other than zero:

DIM DataPtr AS INTEGER PTR

DIM z%(1990 TO 1998, -1 TO 3)

DataPtr = VARPTR(z%(1990, -1))

FOR y = 0 TO 4

FOR x = 0 TO 8

Value% = @DataPtr[x OF 8, y OF 4]

NEXT x

NEXT y

If you subtract the lower bound from itself and the upper bound (to get a lower bound of zero), you get 8 for the upper bound, which is then used for limit after the OF keyword.

See Also

Pointers (@)

Pointers to ASCIIZ and fixed-length strings

Pointers to arrays


Constants and Literals

A string literal is simply a group of characters surrounded by quotation marks. For example:

"This is a string"

"3.14159"

"John Jones, Attorney at Law"

A string literal can include the double-quote character, simply by doubling the character within the string. For example:

A$ = "This is a ""string"""

Numeric literals 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. The amount of precision you supply determines the internal representation (Integer, Long-integer, Quad-integer, Byte, Word, Double-word, Single-precision, Double-precision, Extended-precision, and Currency) which PowerBASIC will use in processing that literal value.

You can also force a literal value to be stored with a given precision by following the constant with one of the variable type-specifiers (%, &, &&, ?, ??, ???, !, #, ##, @, @@). This ability becomes very important when working with Currency and other floating-point numbers.

For example, the statement eVar## = 1.1 stores the Single-precision representation of 1.1 (which is 1.10000002384185791) in the Extended-precision variable eVar##. In order to store the exact quantity 1.1 in eVar##, you must follow 1.1 with the Extended-precision type-specifier (##). For example

DIM x1 AS EXT, x2 AS EXT

x1 = 1.1 ' Single-precision literal

x2 = 1.1## ' Extended-precision literal

a$ = STR$(x1,18) ' 1.10000002384185791

b$ = STR$(x1) ' 1.10000002384186

c$ = STR$(x2,18) ' 1.1

d$ = STR$(x2) ' 1.1

If a type-specifier does not follow a numeric constant, the following rules are used to determine the precision the value will be stored in:

  1. If the value contains no decimal point and is in the range 0 to 255, PowerBASIC stores the value as a Byte.

  2. If the value is an integer in the range -32,768 to 32,767, yet outside the range for Byte constants, PowerBASIC stores the value as an Integer.

  3. If the value is an integer in the range 32,768 to 65,535, PowerBASIC will store the value as a Word.

  4. If the value is an integer in the range -2^31 to 2^31-1 inclusive (about -2 billion to +2 billion), yet outside the range for Word constants, PowerBASIC stores the value as a Long-integer.

  5. If an integer value is positive, exceeds the maximum value for a Long-integer, and still falls within the range for a Double-word, PowerBASIC will store the value as a Double-word.

  6. If the value is an integer too large to fit in a Long or Double-word, but small enough to fit in a Quad integer, it will be stored as a Quad integer.

  7. If the value contains a decimal point and has up to six significant digits, PowerBASIC stores it as a Single-precision floating-point.

  8. A numeric constant with a decimal point and more than six significant digits, but less than 17, or a whole number too large to be a Quad-integer but small enough to fall within the range of Double-precision floating-point, is stored in Double-precision floating-point format. Larger values (with up to 18 significant digits) are stored in Extended-precision format.

For example:

345.1 ' A Single-precision constant

1.10321 ' A Single-precision constant

1.103213 ' A Double-precision constant

3453212.1234 ' A Double-precision constant

1112223.4445556667 ' An Extended-precision constant

When the sign of an Integer constant is not apparent (there is no type-specifier), PowerBASIC uses the following rules to determine whether to store the value as signed or unsigned:

If the number includes a type-specifier, the value will be signed or unsigned according to the type (specifically: Byte, Word and Double-word are unsigned; Integer, Long-integer, Quad-integer are signed).

If there is no type-specifier, and the number is a 16-bit quantity expressed as exactly 4 hexadecimal digits (or the number is a 32-bit quantity expressed as exactly 8 hexadecimal digits) and the most significant bit is set, the value is considered to be signed. All other hexadecimal constants are evaluated as unsigned. The same rules apply to 16 and 32 bit binary and octal literals.

Such signed hexadecimal values can be forced to evaluate as unsigned by adding an additional leading zero digit, or by adding a type-specifier suffix. This behavior is designed for compatibility with other BASIC dialects.

Some examples of these rules follow:

32767?? ' A Word constant (unsigned)

-40000 ' A Long-integer constant (signed)

32 ' A Byte constant (unsigned)

-32 ' An Integer constant (signed)

&H08000 ' A Word constant (unsigned)

&H8000 ' An Integer constant (signed)

&H8000& ' An Integer constant (signed)

&H08000& ' An Integer constant (signed)

&H80000000 ' Long-integer constant (signed)

&H80000000&& ' Long-integer constant (signed)

&H080000000 ' Double-word constant (unsigned)

&H80000000??? ' Double-word constant (unsigned)

This sequence of events allows PowerBASIC to make an intelligent decision about the constants in your program. Rather than arbitrarily making all ambiguous constant references signed (or unsigned), it tries to determine what type of constant you intended to use. You do not necessarily have to make a decision about the size of an integer constant, only whether it should be signed or unsigned. All you need to do to guarantee that a constant will be treated as an unsigned value is to place a leading zero in the number. You need not consider whether the number is a Byte, Word, or Double-word (as long as you do not exceed the largest Double-word value).

It is sometimes convenient to express integers in number systems (bases) other than decimal (which is base 10). This is particularly true when expressing information that is binary in nature; for example, machine addresses. PowerBASIC allows you to specify integer data in Hexadecimal (base 16), Octal (base 8), and Binary (base 2) notation.

Hexadecimal constants consist of up to 16 characters, where each character is from the set 0 through 9 and A through F (and a through f), and must be preceded by &H. An additional (leading) zero can also be included to force the hexadecimal value to be treated as an unsigned value, or a suitable type-specifier can be added instead. The following are equivalent ways of specifying an unsigned hexadecimal value in the Double-word (DWORD) range:

A??? = &H0FFFFFFFF

A??? = &HFFFFFFFF???

Octal constants contain only the characters 0 through 7, can be up to 22 digits long, and must be preceded by &O, &Q, or simply &.

B = &Q7777

Binary constants contain only 0s and 1s, can be up to 64 digits long, and must be preceded by &B.

Each of the following constants represents the integer value 256 (decimal):

256 ~ &H100 ~ &O400 ~ &Q400 ~ &400 ~ &B100000000

Just as with decimal constants, a hexadecimal, octal or binary constant may have a type-specifier. You can use ?, ??, ???, %, &, && (Byte, Word, Double-word, Integer, Long-integer, Quad-integer) type-specifiers with constants in any number base.

If you do not use a type-specifier with these constants, the compiler will select the smallest integer type that will contain the number. When there is no type-specifier, PowerBASIC stores the value as unsigned, unless the most significant bit is a sign bit and the leading digit is not a zero. For example, &H8000 is signed, because its most significant bit is a sign bit (1000 0000 0000 0000), and its leading digit is non-zero. On the other hand, &H08000 is unsigned; although its most significant bit is a sign-bit (1000 0000 0000 0000), its leading digit is a zero.

You can use the VAL function to convert strings to numeric values. Such strings can contain decimal, hexadecimal, binary and octal numbers in string format. See VAL function for more info

Constants and Literals

PowerBASIC programs process two distinct classes of data: variables and constants. A variable is allowed to change its value as a program runs. A constant's value is fixed at compile-time, and cannot change during program execution (hence, it remains constant). PowerBASIC supports four types of constants: string literals, numeric literals, string equates and numeric equates.

String literals

Numeric literals

Integer constants in binary, octal, and hexadecimal

Numeric Equates

String Equates

See Also

Defining Constants

Array Data Types

Bit Data Types

GUID Data Types

Object Data Types

User Defined Types

Unions

Variant Data Types


Defining Constants

PB/CC constants (also known as equates) are defined by prefixing the name of the constant with a "%" character. MSBASIC and VB define constants with the CONST keyword. The MSBASIC/VB compiler then does type conversions at the point of use, if the constant's type was not specified. That overhead does not happen (and is not necessary) with PB/CC. String equates are specified with a leading "$" character.

However, the MACRO facilities in PB/CC offer a way to retain the CONST syntax in your code, while maintaining the low overhead advantage of PowerBASIC. For example:

MACRO CONST = MACRO

...

CONST Something = 1&

CONST Something_Else = 2???

CONST AppTitle = "My Application"

...

PRINT AppTitle, Something

During compilation, the CONST keyword is replaced by the MACRO word, which dynamically creates a new macro that, in turn, defines a constant.

See Also

Upgrading To PBCC

Graphics

Constants and Literals

Numeric Equates

Built-in numeric equates

String Equates

Built-in string equates


Numeric Equates

PowerBASIC allows you to refer to numeric constants by name. Be aware that equates have global scope; that is, they are visible throughout your program. Unlike variables, you can use an equate on the left side of an assignment statement only once, and only a constant value (or a simple variable expression) may be assigned to it. If an expression is used, all parts of the expression must consist of constants, numeric equates; bitwise operators like AND, OR; and NOT; the arithmetic operators +, -, *, /, and \, and the relational operators >, <, >=, <=, <>, =; and the CVQ function. For example, the following are all legal equate definitions:

%X = 1

%Y = 1 + 1

%Z = %X * %Y

%Q = (1& OR 2&) + (NOT 0)

%R = (%Q <> 100&)

%S = CVQ("DemoOnly")

A value must be assigned to each equate before it is referenced, even if that value is zero. If you fail to define an equate, an error will be generated during compilation. When assigning a value to an equate, the line of code is never executed. It is scanned at compile-time and therefore does not have to be located inside of a Sub or Function. For readability, we suggest placing equates at the top of your code.

You can also 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. Equates are particularly well suited for making programs more readable. For example, consider an array to track chess pieces. If we define:

%MAXPIECES = 32
%NPARAM = 3

%NTYPE = 1

%RANK = 2

%FILE = 3

%KING = 1

%PAWN = 2

…we can then define an array of pieces and make statements like the following:

DIM piece(1:%MAXPIECES, 1:%NPARAM)

piece(1, %NTYPE) = %KING

piece(1, %RANK) = 4

piece(1, %FILE) = 1

This sets up a 32 x 3 array for piece information. The first element is the type of unit, the second and third give its current position on the board. Note how much more readable this is than:

DIM piece(1:32, 1:3)

piece(1, 1) = 1

piece(1, 2) = 4

piece(1, 3) = 1

We could achieve a similar effect by using comments, but there is no way to ensure that when the program changes, the comments will be updated. Using equates reduces the need for comments.

Besides being more readable, equates allow us to easily change a program by changing only the definition of a single equate, rather than changing every occurrence of a particular value. For example: say you run a preschool, and you want to keep track of some data that depends on how many kids you have. Furthermore, you have to print out reports each week. Rather than type the number in several places, only to have to change it every week, you can assign the number to a constant.

%NUMKIDS = 28

Then, you can use the constant, %NUMKIDS, throughout your program.

' Calculate income; the enrollment fee is $85 a week;

' Parents pay whether their kids miss days or not

income% = %NUMKIDS * 85

' Calculate actual attendance

attend% = %NUMKIDS - absent%

' Calculate how much the lunches cost per kid; note the

' use of another constant for cost; it may vary too!

perkid% = %LUNCHCOST / attend%

' Calculate net profit per kid after paying for lunches (you'd

' actually have far more overhead than this, but we'll keep it simple)

net% = (income% - perkid%) / %NUMKIDS

' and so on

If your enrollment stays stable, you still have a program that is much easier to follow. Moreover, if your enrollment changes, you only need to change the constant assignment statements to run a revised program. Think of the time you will save - enough to take the kids on an extra field trip.

You might also want to assign the value of an equate conditionally, using the #IF metastatement. For example:

%BIGCLASS = 1

#IF %BIGCLASS

%NUMKIDS = 40

#ELSE

%NUMKIDS = 20

#ENDIF

Equates make SELECT statements more readable too:

SELECT CASE piece(x, %NTYPE)

CASE %KING

{ process king moves }

CASE %PAWN

{ process pawn moves }

CASE %QUEEN

...

END SELECT

This code will continue make sense when you return to it after a long absence.

Numeric equates may be assigned a specific if the literal value has a type-specifier appended. For example:

%MAX_BYTE = 255?

%MAXIMUM_INT = 32767%

%MAXIMUM_DWORD = &HFFFFFFFF???

%MAXIMUM_LONG = &H7FFFFFFF&

%MINIMUM_LONG = &H80000000&

Numeric equates which are derived from an equation are pre-calculated by PowerBASIC during the compilation process, to ensure that unnecessary calculations are eliminated from the executable code. If this optimization was not performed, PowerBASIC code would need to perform the same calculation every time the equate was used in the code. Examples of numeric equates derived from expressions follows:

%WHATEVER1 = 10

%WHATEVER2 = (%WHATEVER1 * 3) + 1

%DEBUG = -1&

%RELEASE = NOT %DEBUG

%DEMO = %RELEASE AND (NOT %DEBUG)

During compilation the actual numeric value of %WHATEVER2 is pre-calculated as 31, and the values of %RELEASE and %DEMO are calculated from the value of %DEBUG. Note that operators like AND and OR work as bitwise operators, rather than logical operators, in numeric equate assignments.

Duplicate definitions of both numeric and string equates are permitted by PowerBASIC, provided the actual equate content is identical. If the content is not identical, a compile-time Error 468 ("Duplicate Equate") will occur.

See Also

Constants and Literals

Defining Constants

Built-in numeric equates

String Equates

Built-in string equates


Built-in numeric equates

The compiler provides a convenient set of built-in numeric equates.

For determining the compiler version (see %DEF for more information):

%PB_CC32, %PB_DLL16, %PB_DLL32, %PB_EXE, %PB_REVISION, %PB_REVLETTER, %PB_WIN32

For use with ARRAYATTR:

%VARCLASS_BYT, %VARCLASS_WRD, %VARCLASS_DWD, %VARCLASS_INT, %VARCLASS_LNG, %VARCLASS_QUD, %VARCLASS_SNG, %VARCLASS_DBL, %VARCLASS_EXT, %VARCLASS_CUR, %VARCLASS_CUX, %VARCLASS_VRNT, %VARCLASS_IFAC, %VARCLASS_GUID, %VARCLASS_TYPE, %VARCLASS_ASC, %VARCLASS_FIX, %VARCLASS_STR, %VARCLASS_BIT, %VARCLASS_SBT, %VARCLASS_FLD

For use with the GRAPHICS statements and the XPRINT statements.:

%BLACK, %BLUE, %GREEN, %CYAN, %RED, %MAGENTA, %YELLOW, %WHITE, %GRAY, %LTGRAY

For use with ERR and ERRCLEAR:

%ERR_NOERROR, %ERR_ILLEGALFUNCTIONCALL, %ERR_OVERFLOW (reserved), %ERR_OUTOFMEMORY, %ERR_SUBSCRIPTPOINTEROUTOFRANGE, %ERR_DIVISIONBYZERO (reserved), %ERR_DEVICETIMEOUT, %ERR_INTERNALERROR, %ERR_BADFILENAMEORNUMBER, %ERR_FILENOTFOUND, %ERR_BADFILEMODE, %ERR_FILEISOPEN, %ERR_DEVICEIOERROR, %ERR_FILEALREADYEXISTS, %ERR_DISKFULL, %ERR_INPUTPASTEND, %ERR_BADRECORDNUMBER, %ERR_BADFILENAME, %ERR_TOOMANYFILES, %ERR_DEVICEUNAVAILABLE, %ERR_COMMERROR, %ERR_PERMISSIONDENIED, %ERR_DISKNOTREADY, %ERR_DISKMEDIAERROR, %ERR_RENAMEACROSSDISKS, %ERR_PATHFILEACCESSERROR, %ERR_PATHNOTFOUND, %ERR_OBJECTERROR, %ERR_GLOBALMEMORYCORRUPT (formerly %ERR_FARHEAPCORRUPT), %ERR_STRINGSPACECORRUPT

For use with GRAPHIC COPY, GRAPHIC GET MIX, GRAPHIC SET MIX, GRAPHIC STRETCH, XPRINT COPY, XPRINT GET MIX, XPRINT SET MIX, and XPRINT STRETCH (some statements may accept only a subset of these equates):

%MIX_BLACKNESS, %MIX_NOTMERGESRC, %MIX_MASKNOTSRC, %MIX_NOTCOPYSRC, %MIX_MASKSRCNOT, %MIX_NOT, %MIX_XORSRC, %MIX_NOTMASKSRC, %MIX_MASKSRC, %MIX_NOTXORSRC, %MIX_NOP, %MIX_MERGENOTSRC, %MIX_COPYSRC, %MIX_MERGESRCNOT, %MIX_MERGESRC, %MIX_WHITENESS

For use with VARIANTVT:

%VT_EMPTY, %VT_NULL, %VT_I2, %VT_I4, %VT_R4, %VT_R8, %VT_CY, %VT_DATE, %VT_BSTR, %VT_DISPATCH, %VT_ERROR, %VT_BOOL, %VT_VARIANT, %VT_UNKNOWN, %VT_I1, %VT_UI1, %VT_UI2, %VT_UI4, %VT_I8, %VT_UI8, %VT_INT, %VT_UINT, %VT_VOID, %VT_HRESULT, %VT_PTR, %VT_SAFEARRAY, %VT_CARRAY, %VT_USERDEFINED, %VT_LPSTR, %VT_LPWSTR, %VT_RECORD, %VT_FILETIME, %VT_BLOB, %VT_STREAM, %VT_STORAGE, %VT_STREAMED_OBJECT, %VT_STORED_OBJECT, %VT_BLOB_OBJECT, %VT_CF, %VT_CLSID, %VT_VECTOR, %VT_ARRAY, %VT_BYREF

See Also

Constants and Literals

Numeric Equates

String Equates

Built-in string equates


String Equates

You can define equates by prefixing a dollar-sign ($) to the equate data name. The value on the right side of the string equate must be a string literal, or an expression made of string literals. A string literal can also be constructed with combinations of the expanded CHR$ function, the STRING$ function, the SPACE$ function, the GUID$ function, and string constants. For example:

$Name = "John Smith"

$Fullname = "John" & " Smith"

$UserName = $First & $Last

$PrintCode = CHR$(27, 34, "E") + SPACE$(10) + CHR$(65 TO 90)

$AppGuid = GUID$("{01234567-89AB-CDEF-FEDC-BA9876543210}")

A string equate can include the double-quote character, simply by doubling the character within the string. For example:

$ABC = "This is a ""string"""

String equates are individually limited to 255 characters. Attempting to create a longer string equate will trigger a compile-time Error 489 ("Invalid string length").

As with numeric equates, PowerBASIC pre-calculates the string equate content during compilation to avoid unnecessary concatenation operations at run-time. Duplicate definitions of both numeric and string equates are permitted by PowerBASIC, provided the actual content is identical. If the content is not identical, a compile-time Error 468 ("Duplicate Equate") will occur.

See Also

Constants and Literals

Defining Constants

Numeric Equates

Built-in numeric equates

Built-in string equates


Built-in string equates

The compiler also provides a set of built-in string equates, including:

Equate | Character(s) | Definition
$NUL | CHR$(0) | Null
$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) | CR and LF
$EOF | CHR$(26) | End-of-File
$ESC | CHR$(27) | Escape
$SPC | CHR$(32) | Space
$DQ | CHR$(34) | Double-Quote

See Also

Constants and Literals

Numeric Equates

Built-in numeric equates

String Equates


Bit Data Types

TYPE and UNION structures may contain bit variables, which are named BIT (unsigned values) or SBIT (signed values). Each bit variable may occupy from 1 to 31 bits. When used in a TYPE, bit variables are packed one after another up to a total of 32 bits per bit field. When used in a UNION, all bit variables overlay each other, starting at the first bit position.

Bit variables may only be used as TYPE or UNION members, not as scalar, array, or pointer variables. The size of a bit variable is defined as:

var AS BIT * nlit [IN BYTE|WORD|DWORD]

where the term "* nlit" defines the number of bits (1 to 31), and the optional term "IN BYTE|WORD|DWORD", if present, defines the start of a new bit field of 1, 2, or 4 bytes.

TYPE abcd

valu AS BIT * 31 IN DWORD

sign AS SBIT * 1

nybl2 AS BIT * 4 IN BYTE

nybl1 AS BIT * 4

END TYPE

The example type above is 5 bytes in size, containing a 4-byte bit field and a 1-byte bit field. In this case, each contain 2 bit variables of varying size. The range of values which may be stored depends upon the number of bits available. For example, "BIT * 4" has a range of 0 to 15, "SBIT * 1" has a range of -1 to 0, and "SBIT * 5" has a range of -16 to +15. BIT and SBIT variables may not be used with SHIFT or ROTATE statements.

UNION abcde

Part1 AS BIT * 8 IN DWORD

Part2 AS BIT * 16

END UNION

The example union above is 2 bytes in size, containing an 8-bit field and an overlapping 16-bit field.

See Also

TYPE/END TYPE block

UNION/END UNION statements


GUID Data Types

PowerBASIC introduces another new variable class: GUID variables. These are a special form of 16-byte string that are used to contain a 128-bit Globally Unique Identifier (GUID), primarily for use with COM Objects.

Generally speaking, a GUID variable is assigned a value with the GUID$ function, or with a string equate, and that value usually remains constant throughout the program. The GUID variable is typically used only as a parameter, rather than as a term in an expression.

GUID variables must be explicitly declared with DIM, LOCAL, etc, and are used in much the same way as a 16-byte fixed-length string or a user-defined type of that size. A GUID variable is only valid as a parameter when its 16 bytes of data are in an appropriate format. For example:

$idNull = STRING$(16,0)

...

DIM abc AS LOCAL GUID

DIM def AS LOCAL STRING

DIM xyz AS GLOBAL GUID

abc = $idNull

abc = GUID$("{00000000-0000-0000-C000-000000000046}")

xyz = abc

def = GUIDTXT$(xyz)

' def contains "{00000000-0000-0000-C000-000000000046}"

See Also

COM Programming Introduction

GUID$ function


Object Data Types

Object variables may only contain a reference to a COM Object, and no other value of any kind. An object variable must be declared by its specific class, which could be the generic "DISPATCH" class, or one that is explicitly defined with an INTERFACE structure. For example:

' Generic Dispatch Object variable

DIM oWord AS DISPATCH

' Interface-specific Object variable

DIM oExcel AS "Excel.Application"

An object variable may only be used in specific situations, such as execution of an Object Method. It is never legal to reference Object variables in normal numeric or string expressions, nor is it possible to even output their value without the use of the special new functions like OBJPTR.

A Dispatch Object is one that communicates through the IDispatch interface, which is the most flexible method, as it supports both named and optional parameters. While an object may support hundreds of functions (known as Methods or Properties) in Dispatch Objects, they are all accessed through a single entry point, called Invoke.

So how does the object tell one from another? By encoding every function (Method or Property), and every named parameter as a special numeric, Long-integer value known as a Dispatch ID, or DispID for short. When a Dispatch Client calls a Method in a Dispatch Object, it passes one or more DispID's to tell the object precisely which Method and which parameters it expects to utilize. Importantly, PowerBASIC handles most all of these messy details for you, so all you really need to know are the names of the functions and parameters.

Dispatch objects require that all parameters, return values, and assignment values be passed only as Variant Variables.

PowerBASIC can access Dispatch Objects through "Early Binding" or "Late Binding". Late binding, the simpler method, offers the most flexibility. You don't need to declare Methods, Properties, and parameters in advance, as everything is resolved at the time the program is executed. A Dispatch Object knows a lot about itself, including information about all of its member functions. However, for that very reason, the validity of these references is not checked at compile time. Virtually everything is done at run time.

See Also

COM Programming Introduction

What is a COM component?

What is a Dispatch Interface?

Methods and Properties

Parameters


Variant Data Types

Variant variables are now supported by PowerBASIC, but their use is limited to that of a parameter assignment for conversion of data, for compatibility with other languages and applications, especially COM Objects.

Although notoriously lacking in efficiency, Variants are commonly used as COM Object parameters due to their flexibility. You can think of a Variant as a kind of container, which can hold a variable of most any data type, , , or even an entire array. This simplifies the process of calling procedures in a COM Object Server, as there is little need to worry about the myriad of possible data types for each parameter.

This flexibility comes at a great price in performance, so PowerBASIC limits their use to data storage and parameters only. You may assign a numeric value, a string value, or even an entire array to a Variant with the LET statement, or its implied equivalent. In the same way, you may assign one Variant value to another Variant variable, or even assign an array contained in a Variant to a compatible PowerBASIC array, or the reverse.

You may extract a simple scalar value from a Variant with the VARIANT# function for numeric values (regardless of the internal numeric data type), or with the VARIANT$ function for string values. You may determine the type of data a Variant variable contains with the VARIANTVT function. The following table summarizes the predefined (built-in) equates that can be used to examine a Variant:

Result

Equate

Content Type

0

%VT_EMPTY

0 or Empty string

1

%VT_NULL

Null string

2

%VT_I2

Integer

3

%VT_I4

Long-Integer

4

%VT_R4

Single

5

%VT_R8

Double

6

%VT_CY

Currency

7

%VT_DATE

Date

8

%VT_BSTR

Dynamic String

9

%VT_DISPATCH

Interface reference

10

%VT_ERROR

Error Code

11

%VT_BOOL

Boolean

12

%VT_VARIANT

Variant

13

%VT_UNKNOWN

16

%VT_I1

Byte

17

%VT_UI1

Byte

18

%VT_UI2

Word

19

%VT_UI4

DWORD

20

%VT_I8

Quad (signed)

21

%VT_UI8

Quad (unsigned)

22

%VT_INT

Integer

23

%VT_UNIT

Word

24

%VT_VOID

25

%VT_HRESULT

COM result code

26

%VT_PTR

Pointer

27

%VT_SAFEARRAY

VB Array

28

%VT_CARRAY

29

%VT_USERDEFINED

30

%VT_LPSTR

ANSI string

31

%VT_LPWSTR

Unicode string

64

%VT_FILETIME

65

%VT_BLOB

66

%VT_STREAM

67

%VT_STORAGE

68

%VT_STREAMED_OBJECT

69

%VT_STORED_OBJECT

70

%VT_BLOB_OBJECT

71

%VT_CF

72

%VT_CLSID

Class ID

&H1000

%VT_VECTOR

&H2000

%VT_ARRAY

Array

&H4000

%VT_BYREF

Variants may not be used in an expression, be directly output (PRINT#, etc), or used as a member of a structure such as a User-Defined Type (UDT) or UNION, etc. Instead, you must first extract the value with one of the above conversion functions, and use that acquired value for calculations.

Internally, a Variant is always 16-bytes in size, and may be passed as either a BYVAL or a BYREF parameter, at the programmer's discretion. However, when a BYREF Variant is required as a parameter, only an explicit Variant variable may be passed by the calling code - a BYCOPY expression is not allowed.

All dynamic strings contained in a Variant must be Wide/Unicode, and PowerBASIC handles these conversions automatically through the LET statement and its implied equivalent.

There may be some cases where you wish to manipulate the internal structure of a Variant directly. Though possible, you must exercise caution or a serious memory leak could occur. Since a Variant could be the owner of a string, array, etc., you must always reset a Variant ([LET] VrntName = EMPTY) prior to manipulation with POKE, or pointers, etc.

When you use the standard PowerBASIC assignment syntax, for example: [LET] VrntName = 21, all this "housekeeping" is completely automatic and handled by PowerBASIC for you.

Every Variant variable must be explicitly declared with an appropriate statement such as:

DIM xyz AS VARIANT or LOCAL xyz AS VARIANT

See Also

COM Programming Introduction

What is a COM component?

What is a Dispatch Interface?

Methods and Properties

Parameters


Comparative Data Types C/C++

When dealing with C, intrinsic types are in lowercase. Defined types are in all caps by convention. C data types are case-sensitive. Integer-class types can take a modifier of "signed" or "unsigned", and are signed by default.

C arrays are defined by the number of elements and are indexed from zero:

"char foo[32]" translates to DIM foo(0 TO 31) AS BYTE, or DIM foo AS STRING * 32, depending on the context of the code.

C arrays are stored in row-major order whereas PowerBASIC arrays are stored in column-major order. Bear in mind that when accessing C arrays the following C code:

k = arr[i,j]

…would translate to PowerBASIC as:

k = arr(j,i)

C arrays are accessed as follows:

(0,0), (0,1), (0,2), ...

(1,0), (1,1), (1,2), ...

…whereas PowerBASIC arrays are accessed:

(0,0), (1,0), (2,0), ...

(0,1), (1,1), (2,1), ...

Commonly, C/C++ code prefixes data types with "LP" which indicates a pointer. Therefore, items with the LP prefix usually correspond to a pointer in PowerBASIC; however, the size of the pointer's target will depend on the data type.

More information on C/C++ syntax can be found on the Internet, such as at http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf and http://www.open-std.org/JTC1/SC22/WG21/

C/C++ Data Types

Type | Language | Format | PowerBASIC
bool | C++ | unsigned 8-bit | BYTE (2)
char | C/C++ | signed 8-bit | BYTE (2)
char* | C/C++ | char pointer | ASCIIZ (1)
double | C/C++ | 8-byte float | DOUBLE
float | C/C++ | 4-byte float | SINGLE
int | C/C++ | signed 32-bit | LONG (3)
long | C/C++ | signed 32-bit | LONG
short | C/C++ | signed 16-bit | INTEGER
void | C/C++ | (no return value) | SUB
void * | C/C++ | pointer | (ANY) [PTR] (1)

Defined types (SDK types)

Type

Format

PowerBASIC

ATOM

unsigned 16-bit

WORD

BOOL

signed 32-bit

LONG

boolean

8-bit integer

BYTE

Boolean

signed 16-bit

INTEGER

BOOLEAN

8-bit integer

BYTE

BSTR

dynamic string

STRING {unicode}

BYTE

unsigned 8-bit

BYTE

COLORREF

unsigned 32-bit

DWORD

DWORD

unsigned 32-bit

DWORD

HANDLE

unsigned 32-bit

DWORD

HWND/HDC/…

unsigned 32-bit

DWORD

INT32

signed 32-bit

LONG

INT64

signed 64-bit

QUAD

LARGE_INTEGER

signed 64-bit

QUAD

LPARAM

signed 32-bit

LONG

LP…

pointer

(ANY) [PTR] (4)

LPCSTR

ASCIIZ pointer

ASCIIZ [PTR]

LPDWORD

DWORD pointer

DWORD [PTR]

LPINT

LONG pointer

LONG [PTR]

LPSTR

ASCIIZ pointer

ASCIIZ [PTR]

LPUINT

DWORD pointer

DWORD [PTR]

LPVOID

32-bit pointer

(ANY) [PTR]

LRESULT

signed 32-bit

LONG

NULL

32-bit

0 or %NULL

PASCAL

{calling convention}

/STDCALL

QWORD

unsigned 64-bit

QUAD (2)

STDCALL

{calling convention}

SDECL/STDCALL

UCHAR

unsigned 8-bit

BYTE

UINT

unsigned 32-bit

DWORD (3)

UINT16

unsigned 16-bit

WORD

UINT32

unsigned 32-bit

DWORD

UINT64

unsigned 64-bit

QUAD (2)

VOID

SUB

{no return value}

VOID *

pointer

(ANY) [PTR] (1)

WINAPI

{calling convention}

SDECL/STDCALL

WORD

unsigned 16-bit

WORD

WPARAM

signed 32-bit

LONG


Comparative Data Types Delphi

Delphi uses integer conventions similar to C, although the names are case-insensitive, as with BASIC. That is, a Delphi INTEGER value may be either a PowerBASIC INTEGER or LONG, depending on whether the Delphi code is 16-bit or 32-bit.

The elements of multi-dimensional arrays, in Delphi, are not necessarily stored in a straightforward order in memory. Such arrays are not compatible with other languages.

Delphi Data Types

Type | Format | PowerBASIC
boolean | unsigned 8-bit | BYTE
byte | unsigned 8-bit | BYTE
bytebool | unsigned 8-bit | BYTE
cardinal | unsigned 16/32-bit | WORD/DWORD (5)
comp | signed 64-bit | QUAD
currency | 8-byte fixed point | CURRENCY
double | 8-byte floating point | DOUBLE
extended | 10-byte floating point | EXT
int64 | signed 64-bit | QUAD
integer | signed 16/32-bit | INTEGER/LONG (5)
longbool | signed 32-bit | LONG
longint | signed 32-bit | LONG
longword | unsigned 32-bit | DWORD
pchar | ASCIIZ string | ASCIIZ
shortint | signed 8-bit | BYTE (2)
single | 4-byte float | SINGLE
smallint | signed 16-bit | INTEGER
variant | data-dependent | VARIANT
word | unsigned 16-bit | WORD
wordbool | unsigned 16-bit | WORD


Comparative Data Types Visual Basic 6

Visual Basic Data Types

Type | Format | PowerBASIC
Boolean | signed 16-bit | INTEGER
Byte | unsigned 8-bit | BYTE
Const | numeric constant | {Equate} (2)
Currency | 8-byte fixed point | CURRENCY
Double | 8-byte float | DOUBLE
Integer | signed 16-bit | INTEGER
Long | signed 32-bit | LONG
Single | 4-byte float | SINGLE
String | dynamic string | STRING
String * n | fixed-length string | STRING * n
Variant | data-dependent | VARIANT


Default Variable Typing

In PowerBASIC/DOS and MSBASIC, all variables without a type-specifier (%, !, &, etc.) default to Single-precision. In Visual Basic, all untyped variables default to Variants.

In PB/CC, variables without a type specifier are not allowed unless you specifically tell the compiler the default type using the DEF statement. For example, to mimic the Single-precision default of PB/DOS, simply add a DEFSNG statement to the top of your code:

DEFSNG A-Z

See Also

Upgrading To PBCC

Variables

Variable Scope

THREADED variables

LOCAL, GLOBAL and STATIC considerations


Variable Scope

All variables used inside a SUB or FUNCTION are local by default. This means that any Sub or Function which is called by another Sub or Function will not see the values of the caller’s local variables, even if the called Sub or Function has variables with the same name. Local variables are stored on the stack, so when a Sub or Function ends, the memory allocated for all local variables is released.

You can share variables between all of the Subs and Functions by using the GLOBAL keyword in your code. In the following example, the Message variable is shared by all Subs. Hello calls Init to initialize the Message variable with the word "Hello!" and then Display to show it. It then assigns the value "Goodbye!" to Message and again calls Display to show it. Because Caption is defined as local to Init, it is never seen by the Display Sub:

SUB Init()

GLOBAL Message AS STRING ' Declare Message as global

LOCAL Caption AS STRING ' Declare Caption as local

Caption = "GLOBAL Test"

Message = "Hello!"

END SUB

SUB Display()

#IF %DEF(%PB_CC)

PRINT Caption$, Message$

#ELSE

MSGBOX Message$,,Caption$

#ENDIF

END SUB

FUNCTION PBMAIN() AS LONG

CALL Init

CALL Display

Message = "Goodbye!"

CALL Display

END FUNCTION

Global variables retain their value after a Sub or Function ends.

All Subs and Functions see global variables in your code, but external DLLs or executables do not see them. In order to use a variable from another DLL or EXE it must be passed as a parameter.

It is also possible to create variables that are local to a Sub or Function, but do not lose their value when the Sub/Function ends. These variables are called Static, and the STATIC keyword is used to create them. In the following example the Function AddTwo is called 5 times and the result is returned to the caller:

DECLARE FUNCTION AddTwo%()

FUNCTION PBMAIN() AS LONG

LOCAL z%

FOR z% = 1 TO 5

#IF %DEF(%PB_CC32)

PRINT AddTwo%

#ELSE

MSGBOX STR$(AddTwo%)

#ENDIF

NEXT z%

END FUNCTION

FUNCTION AddTwo%()

STATIC Result%

Result% = Result% + 2

FUNCTION = Result%

END FUNCTION

When AddTwo is first called, Result% has a value of zero and it returns a value of two. Each time it is called thereafter, the old value is incremented by two. The final result is 10, which is passed back to the caller.

See Also

Variables

Default Variable Typing

THREADED variables

LOCAL, GLOBAL and STATIC considerations


THREADED variables

PowerBASIC now offers a THREADED variable type for use in multi-threaded programs. While multi-threaded programming is an advanced topic and is beyond the scope of this documentation, we'll briefly describe thread variables in PowerBASIC.

Thread variables (also known as thread-local storage or TLS) can be declared with either a THREADED statement or a DIM statement (with the THREADED scope clause). In either case, thread variables are visible to all Subs and Functions in a program, but each thread will have its own unique copy of each of the variables. This allows data to be shared between Subs and Functions on a thread by thread basis. For example:

THREADED t1 AS LONG

...

FUNCTION MyThread(BYVAL x AS LONG) AS DWORD

t1 = RND(1,x)

' Here t1 is unique to each thread

CALL MySub

END FUNCTION

...

SUB MySub

? t1

' Here t1 is unique to the thread calling MySub

END SUB

Thread variables have a distinct advantage over global variables as they avoid the need to use synchronization techniques (such as a Critical Section object or Mutex) when referencing (reading or writing) from two or more threads at the same time. In addition, all allocation and deallocation of THREADED memory is handled automatically by PowerBASIC without any need for intervention by the programmer. However, by their nature, thread variables impose a slight, yet measurable, performance penalty as compared to other variable types.

See Also

Variables

Default Variable Typing

Variable Scope

LOCAL, GLOBAL and STATIC considerations


LOCAL, GLOBAL and STATIC considerations

All LOCAL variables are stored on the stack frame of the Sub/Function in which they are defined. Therefore, the address of local variables may vary with each invocation of the Sub/Function in which they are defined (because they are created each time the Sub/Function is executed, and destroyed upon exit). It is unsafe to return a to a local variable, since the storage for that variable is released when the Sub/Function ends.

Conversely, GLOBAL and STATIC variables are stored in the main data memory area, so their address stays constant for the duration of the program module, so returning a pointer to a GLOBAL or STATIC variable is quite safe.

Typically, the stack frame of a PowerBASIC application can hold close to 1 MB of LOCAL data (also see #STACK). Therefore, care must be exercised when large local ASCIIZ and/or fixed-length strings are used, to ensure that these strings do not exceed the available stack space when the Sub/Function executes. This means that very large ASCIIZ and fixed length strings may need to be changed to a STATIC or GLOBAL type instead, since those types are not created within the stack frame and will not overflow the stack.

Local dynamic (variable length) strings are not stored in the same manner as ASCIIZ and fixed-length strings. Every dynamic string has an associated 4 byte "string handle", and this handle is stored in the stack frame, but the actual string data is stored in the main data memory area. Therefore, local dynamic strings occupy just 4 bytes of the stack frame regardless of whether the string data itself comprises just one byte or 100 Megabytes.

Similarly, local arrays have an associated "array descriptor" table. This small table is stored in the stack frame, and the actual array data is stored in the main data memory area. Therefore, arrays also have only a very small impact on the stack frame.

It should also be noted that when a DIM statement is used (without an explicit scope clause), to declare a variable (or array) in a Sub or Function, and an identical variable (or array) has already been declared as GLOBAL, the variable in the Sub/Function will be given GLOBAL scope. For example:

GLOBAL xyz AS LONG

...

SUB MySub

DIM xyz AS LONG

' Here, xyz is a GLOBAL variable

END SUB

To ensure that the variable scope is LOCAL to the Sub/Function, use a LOCAL statement rather than a DIM statement. Alternatively, add an explicit scope clause to the DIM statement. For example:

GLOBAL xyz AS LONG

...

SUB MySub

DIM xyz AS LOCAL LONG

' Here, xyz is a LOCAL variable

END SUB

Finally, it should be noted that the DIM statement (without an explicit scope clause) does not affect identically named THREADED variables as is it does with GLOBAL variables. For example:

THREADED abc AS LONG

...

SUB MySub

DIM abc AS LONG

' Here abc is a LOCAL variable, not a THREADED variable

END SUB

See Also

Variables

Default Variable Typing

Variable Scope


Arithmetic Operators

Arithmetic operators perform normal mathematical operations. Several of these operators merit a word of explanation. The backslash (\) represents integer division. Integer division rounds its operands to integers, to produce a truncated quotient with no remainder. For example, 5 \ 2 evaluates to 2, and 9 \ 10 evaluates to 0. Integer division is also faster than floating-point division when using integer-class variables or expressions.

The remainder of an integer division can be determined with the MOD (modulo) operator (MOD is valid for all numeric types). MOD is similar to integer division except that it returns the remainder of the division rather than the quotient. For example, 5 MOD 2 returns the value 1, and 9 MOD 10 returns the value 9.

The ISTRUE operator returns TRUE only if its operand is TRUE (non-zero). ISTRUE is guaranteed to return -1 as its TRUE value, whereas the operators can return any non-zero value.

The ISFALSE operator returns TRUE only if its operand is FALSE (zero). ISFALSE is guaranteed to return -1 as its TRUE value, where the operators can return any non-zero value.

PowerBASIC arithmetic operators

Operator | Action | Example
^ | Exponentiation | 10^4
- | Negation | -16
* | Multiplication | 45 * 19
/ | Floating-point division | 45 / 19
\ | Integer division | 45 \ 19
MOD | Modulo | 45 MOD 19
+ | Add | 45 + 19
- | Subtract | 45 - 19
ISFALSE | Boolean False | ISFALSE 45
ISTRUE | Boolean True | ISTRUE 19
NOT, AND, OR, XOR, EQV, IMP | Bit manipulation operations | NOT 0, 45 AND 19 45 OR 19, 45 XOR 19 45 EQV 19, 45 IMP 19

Note: PowerBASIC does not trap numeric overflow or underflow errors in equation and expression evaluation. Please refer to the chapter Errors and Error Trapping for more information.

It is recommended that this table be read in conjunction with the Mathematical Order of Operator Precedence table, and the effects that operator precedence has on the evaluation of numeric expressions.

See Also

Relational Operators

Operator Precedence


Relational Operators

Relational operators allow you to compare the values of two expressions, to obtain a Boolean result of TRUE or FALSE. Although they can be used in any expression (for example, a = (b > c) / 13), the numeric results returned by relational operators are generally used in an or other decision statements, to make a judgment regarding program flow.

PowerBASIC relational operators

Operator | Relation | Example
= | Equality | 5 = 5
<>, >< | Inequality | 5 <> 6
< | Less than | 5 < 6
> | Greater than | 6 > 5
<=, =< | Less than or equal to | 5 <= 6
>=, => | Greater than or equal to | 6 >= 5

When arithmetic and relational operators are combined in an expression, arithmetic operations are always evaluated first. For example, 4 + 5 < 4 * 3 evaluates to TRUE (non-zero), because the arithmetic operations (addition and multiplication) are carried out before the relational operation. This then tests the truth of the assertion 9 < 12.

Strings and relational operators

PowerBASIC lets you compare data. String expressions can be tested for equality, as well as for "greater than" and "less than" alphanumeric ordering.

Two string expressions are equal if and only if they contain exactly the same characters in exactly the same order. For example:

a$ = "CAT"

x1% = (a$ = "CAT") : x2% = (a$ = "CATS") : x3% = (a$ = "cat")

String ordering is based on two criteria: first, the ASCII values of the characters they contain, and second, the length of the strings.

For example, the letter A is less than the letter B because the ASCII code for A, 65, is less than the code for B, 66. Note, however, that B is less than a because the ASCII code for each lowercase letter is greater than the corresponding uppercase character (exactly 32 greater). When comparing mixed uppercase and lowercase information, use the UCASE$ or LCASE$ functions to keep case differences from interfering with the test.

city1$ = "Seattle"

city2$ = "Tucson"

IF UCASE$(city1$) > UCASE$(city2$) THEN

city$ = city1$

ELSE

city$ = city2$

END IF

city1$ = UCASE$(city1$)

city2$ = UCASE$(city2$)

IF city1$ > city2$ THEN

city$ = city1$

ELSE

city$ = city2$

END IF

Note the difference between the two sets of statements. In the first case, the string variables city1$ and city2$ are converted to uppercase for the comparison only, so the first IF/THEN returns Tucson. In the second case, the conversion is performed on the variables themselves, so the result will be TUCSON.

Length is important only if both strings are identical up to the length of the shorter string, in which case the shorter one evaluates as less than the longer one; for example, CAT is less than CATS.

The ARRAY SORT and ARRAY SCAN statements allow you to specify whether lower case characters are to be treated as uppercase for comparison purposes. You can also specify a string that explicitly determines the sorting order for all 256 ASCII characters.

See Also

Arithmetic Operators

Operator Precedence


Operator Precedence

Mathematical order of Operator Precedence

  • parentheses ( )

  • exponentiation (^)

  • negation (-)

  • multiplication (*), division (/)

  • division (\)

  • modulo (MOD)

  • addition (+), subtraction (-)

  • relational operators (<, <=, =, >=, >, <>)

  • NOT, ISFALSE, ISTRUE

  • AND

  • OR, XOR (exclusive OR)

  • EQV (equivalence)

  • IMP (implication)

For example, the expression 3+6 / 3 evaluates to 5, not 3. Division has a higher priority than addition, so the division operation (6 / 3) is performed first. Even though the compiler will not get confused, people still could, so a better programming style might be to use 3 + (6 / 3) or 3 + 6/3, either using parentheses or spacing to make the intent clear. Otherwise it is easy to misread the statement as (3 + 6) / 3.

To handle operations of the same priority, PowerBASIC proceeds from left to right. For example, in the expression 4 - 3 + 6, the subtraction (4 - 3) is performed before the addition (3 + 6), producing the intermediate expression 1 + 6.

Operations inside parentheses are of the highest priority and are always evaluated first. Within parentheses, standard precedence is used. Use parentheses like garlic: generously, but not to excess.

Another example of the effect of Order of Precedence on an expression follows:

x = -1^2

At first glance, the result of 1 may be the expected result (since -1 * -1 = 1); however, the unary negation operator has a lower precedence than exponentiation, so the expression is evaluated as x& = -(1^2) which gives a result value of -1. As noted above, the use of parentheses can clarify the intended expression:

x = (-1)^2

See Also

Arithmetic Operators

Relational Operators


Files

PowerBASIC offers three distinct ways to store and retrieve information from disk: sequential, random, and binary file input and output. Each has its advantages and disadvantages; the one that works best for you will depend on your application.

See Also

Sequential Files

Random Access Files

Binary Files


Sequential Files

Sequential file techniques provide a straightforward way to read and write files. PowerBASIC's sequential file commands manipulate text files: files of ASCII characters with carriage-return/linefeed pairs separating records.

Quite possibly, the best reason for using sequential files is their degree of portability to other programs, programming languages, and computers. Because of this, you can often look at sequential files as the common denominator of data processing, since they can be read by word-processing programs and editors (such as PowerBASIC's), absorbed by other applications (such as database managers), and sent over the Internet to other computers.

The idea behind sequential files is simplicity itself: write to them as though they were the screen and read from them as though they were the keyboard.

Create a sequential file using the following steps:

  1. Open the file in sequential output mode. To create a file in PowerBASIC, you must use the OPEN statement. Sequential files have two options to prepare a file for output:

OUTPUT: If a file does not exist, a new file is created. If a file already exists, its contents are erased, and the file is then treated as a new file.

APPEND: If a file does not exist, a new file is created. If a file already exists, PowerBASIC appends (adds) data at the end of that file.

  2. Output data to a file. Use WRITE# or PRINT# to write data to a sequential file.

  3. Close the file. The CLOSE statement closes a file after the program has completed all I/O operations.

To read a sequential file:

  1. First, OPEN the file in sequential INPUT mode. This prepares the file for reading.

  2. Read data in from the file. Use PowerBASIC's INPUT# or LINE INPUT# statements.

  3. Close the file. The CLOSE statement closes a file after the program has completed all I/O operations.

The drawback to sequential files is, not surprisingly, that you only have sequential access to your data. You access one line at a time, starting with the first line. This means if you want to get to the last line in a sequential file of 23,000 lines, you will have to read the preceding 22,999 lines.

Sequential files, therefore, are best suited to applications that perform sequential processing (for example, counting words, checking spelling, printing mailing labels in file order) or in which all the data can be held in memory simultaneously. This allows you to read the entire file in one fell swoop at the start of a program and to write it all back at the end. In between, the information can be stored in an array (in memory) which can be accessed randomly.

Although the SEEK statement can be used to change the point in the file where the next read or write will occur, the calculations required to determine the position of the start of each record in a sequential file would add considerable overhead. Sequential files typically consist of records of varying sizes. Either you would have to maintain a separate index file indicating the starting byte position of each record, or you would have to seek randomly until you found the correct position. However, SEEK does have its uses with sequential files. For instance, after reading an entire file, you could use SEEK to reposition the file pointer to the start of the file, in order to process the data a second time. This is certainly quicker than closing and re-opening the file.

Sequential files lend themselves to database situations in which the length of individual records is variable. For example, suppose an alumni list had a comments field. Some people may have 100 bytes or more of comments. Others, perhaps most, will have none. Sequential files handle this problem without wasting disk space.

Finally, the OPEN statement provides an optional LEN parameter for use with sequential files. This instructs PowerBASIC to use internal buffering to speed up reading of sequential files, using a buffer of the size specified by the LEN parameter. A buffer of 8192 bytes is suggested for best general performance, especially when networks are involved. However, this value can be increased in size to gain additional performance - the best value will always be specific to a particular combination of hardware and software, and may vary considerably from PC to PC, network to network, etc.

See Also

Files

Random Access Files

Binary Files


Random Access Files

Random access files consist of records that can be accessed in any sequence. This means the data is stored exactly as it appears in memory, thus saving processing time (because no translation is necessary) both in when the file is written and in when it is read.

Random files are a better solution to database problems than sequential files, although there are a few disadvantages. For one thing, random files are not especially transportable. Unlike sequential files, you cannot peek inside them with an editor, or type them in a meaningful way to the screen. In fact, moving a PowerBASIC random file to another computer or language will probably require that you write a translator program to read the random file and output a text (sequential) file.

One example of the transportability problem strikes close to home. Interpretive BASIC uses Microsoft's non-standard format for values, and PowerBASIC uses IEEE standard floating-point conventions, this means you cannot read the floating-point fields of random files created by Interpretive BASIC with a PowerBASIC program, or vice versa, without a bit of extra work.

The major benefit of random files is implied in their name: every record in the file is available at any time. For example, in a database of 23,000 alumni, a program can go straight to record number 11,663 or 22,709 without reading any of the other records. This capability makes it the only reasonable choice for large files, and probably the better choice for small ones, especially those with relatively consistent record lengths.

However, random access files can be wasteful of disk space because space is allocated for the longest possible field in every record. For example, a 100-byte comment field forces every record to use an extra 100 bytes of disk space, even if only one in a thousand actually uses it.

At the other extreme, if records are consistent in length, especially if they contain mostly numbers, random files can save space over the equivalent sequential form. In a random file, every number of the same type (Integer, Long-integer, Quad-integer, Byte, Word, Double-word, Single-precision, Double-precision, Extended-precision or Currency) occupies the same amount of disk space, regardless of the value itself. For example, the following five Single-precision values each require four bytes (the same space they occupy in memory):

0

1.660565E-27

15000.1

641

623000000

By contrast, numbers in a sequential file require as many bytes as they have ASCII characters when printed (plus one for the delimiting comma if WRITE# was used instead of PRINT#). For example:

WRITE #1, 0;0 ' takes 3 bytes

PRINT #1, 0;0 ' takes 5 bytes

PRINT #1, 1.660565E-27 ' takes 13 bytes

You can create, write, and read random access files using the following steps:

1. First, OPEN the file and specify the length of each record:

OPEN filespec FOR RANDOM AS [#]filenum [LEN = recordsize]

The LEN parameter indicates to PowerBASIC the total size of each record in bytes. If you do not specify a LEN parameter, PowerBASIC assumes 128. Unlike sequential files, you do not have to declare whether you are opening for input or output because you can simultaneously read and write a random file.

2. Define a structure for records in the file using the TYPE statement.

TYPE StudentRecord

LastName AS STRING * 20 ' A 20-character string

FirstName AS STRING * 15 ' A 15-character string

IDnum AS LONG ' Student ID, a Long-integer

Contact AS STRING * 30 ' Emergency contact person

ContactPhone AS STRING * 14 ' Their phone number

ContactRel AS STRING * 8 ' Relationship to student.

AverageGrade AS SINGLE ' Single-precision % grade

END TYPE

DIM Student AS StudentRecord

3. Fill the UDTs members with the values you want, and write records to the file using the PUT statement.

Student.LastName = "Anderson"

Student.FirstName = "Bob"

Student.IDnum = 494425610

Student.Contact = "Ma Anderson"

Student.ContactPhone = "(800) BOBSMOM"

Student.ContactRel = "Mother"

Student.AverageGrade = 98.9

PUT #fileNumber, recordNumber, Student

4. Read records from the file using the GET statement.

GET #fileNumber, recordNumber, Student

5. When finished, CLOSE the file.

See Also

Files

Sequential Files

Binary Files


Binary Files

PowerBASIC's binary file technique, an extension to Interpretive BASIC, allows you to treat any file as a numbered sequence of bytes without regard to anything, including the following: ASCII characters, number versus string considerations, record length, carriage returns. With the binary approach to a file problem, you read and write a file by specifying exactly which bytes to read or write. This is similar to the services provided by Windows API functions used for reading and writing files.

Flexibility always comes at a price. Binary files require that you do all the work to decide what goes where. Binary may be the best option when dealing with alien files that aren't in ASCII format; for example, a file created by a spreadsheet or database product. Of course, you will have to know the precise structure of the file before you can even attempt to break it down into numbers and strings agreeable to PowerBASIC.

Every file opened in binary mode has an associated position indicator that points to the place in the file that will be read or written to next. Use the SEEK statement to set the position indicator, and the SEEK function to read it.

Binary files are accessed in the following way:

1. First, OPEN the file in BINARY mode. You need not specify whether you are reading or writing; you can do either, or both.

2. To read the file, use SEEK to position the file pointer at the byte you want to read. Then use GET$ to read a specified number of characters into a string variable.

3. To write to the file, load a string variable with the information to be written. Then use SEEK to position the point in the file to which it should be written, and use PUT$ to write the data.

4. When finished, CLOSE the file.

See Also

Files

Sequential Files

Random Access Files


Errors

Error Overview

Unlike the DOS versions of PowerBASIC, Windows versions of PowerBASIC employ a completely different philosophy: to generate the smallest and fastest possible code. Consequently, error handling is placed firmly in the hands of the programmer. This sectrion describes the types of errors that may be encountered, and follows on with a discussion on error detection and error handling techniques.

There are two fundamental types of errors in PowerBASIC: Run-time and Compile-time. Compile-time errors are errors in syntax discovered by the compiler. Run-time errors are anomalies caught at run-time by error-detection mechanisms that the compiler places in your programs.

Examples of Run-time errors include file-system errors (disk full or write-protected), improper function calls ( statements without an installed serial port), memory errors (usually, not enough), and a host of other problems.

See Also

Numeric Errors

Error range

User-defined Errors

Compile-time errors

Run-time errors

Disk Errors


Numeric Errors

In order to generate tight, fast code, we have eliminated quite a bit of error checking that was done in earlier compilers (such as Division-by-Zero, Numeric Overflow, and most other numeric checking errors). While this results in code that is considerably smaller and faster than any other Windows compiler product, it does put more of an onus on the programmer to write code that is bug-free, or code that does its own error checking and validation of its data.

For example, an application that performs exponentiation of a negative value to a fractional power (-5^1.9) will not trigger a run-time error, but the result of the expression will be undefined. Therefore, it makes sense for the application to make some attempt to validate or restrict the numeric range of the arguments of this kind of expression.

See Also

Error Overview

Error range

User-defined Errors

Error Trapping

Compile-time errors

Run-time errors

Disk Errors


Error range

Run-time error values are restricted to the range 1 through 255, and the compiler reserves codes 0 through 150, and 241 through 255 for predefined errors. Attempting to set an error value (with the ERROR statement) outside of the valid range 1 to 255 will result in a run-time Error 5 ("Illegal function call") instead.

See Also

Error Overview

Numeric Errors

User-defined Errors

Compile-time errors

Run-time errors

Disk Errors


User-defined Errors

In addition to the predefined run-time errors, you may also set your own customized run-time error codes in the range 151 through 240. These error codes may be useful to signal specific types of errors in your own applications, ready to be handled by your error trapping code.

See Also

Error Overview

Numeric Errors

Error range

Compile-time errors

Run-time errors

Disk Errors


Compile-time errors

Compile-time errors are generated when the compiler cannot resolve a problem in your source code while it is compiling. Examples include: typographical errors; assigning incorrect values to variables (such as "x$ = 5"); and attempting to use a variable name which has not been dimensioned when OPTION EXPLICIT (or #DIM ALL) has been turned on.

When a compile-time error is detected, PowerBASIC will display a message box indicating the error code, plus a brief description, along with the line number in the code where the error occurred. The offending line of code will also be displayed. If you are using the PowerBASIC IDE, the caret will move to the offending line once the error dialog has been dismissed.

See Also

Error Overview

Run-time errors

Disk Errors

Error Trapping


Error 401 - Expression too long/complex

Expression too long/complex - The expression contained too many ; break it down into two or more simplified expressions.


Error 402 - Statement too long/complex

Statement too long/complex - The statement complexity caused an overflow of the internal compiler buffers; break the statement down into two or more simplified statements. This error can also occur if a SELECT CASE structure using the AS CONST optimization causes the internal jump table to exceed the maximum size (approximately 3200 entries or 12 Kb).


Error 403 - #IF nesting overflow

#IF nesting overflow - Conditional compilation blocks (#IF/#ELSE/#ENDIF) can only be nested up to 16 levels deep.


Error 404 - #INCLUDE file/Macro nesting overflow

#INCLUDE file/Macro nesting overflow - Include files and macros may be nested up to six levels deep. The most common cause of this error stems from excessive nesting and/or circular references. For example, a nested #INCLUDE file that includes itself or an ancestor file that in turn includes the file again, etc. Likewise, a macro that references itself either directly or indirectly can cause a circular reference. See the MACRO statement for more information on the limits of macro expansions.


Error 405 - Block nesting overflow

Block nesting overflow - Your program has too many statement block structures nested within each other. In PowerBASIC block structures may be nested 64 levels deep.


Error 406 - Compiler out of memory

Compiler out of memory - Available compiler memory for symbol space, buffers, and so on, has been exhausted.

If no more memory is available, separate your program into a small main program which uses the #INCLUDE metastatement to include the rest of your program. You can also try the following steps:

  • Remove unnecessary line numbers and labels.

  • Shorten your variable and procedure names.

  • If your code includes WIN32API.INC: Try adding the "code exclusion" equates such as %NOGDI = 1 to your code to cause the compiler to ignore large sections of the API file. Please review the first few pages of notes in WIN32API.INC for more information.

Alternatively, create a customized version of WIN32API.INC that contains just the definitions and declarations actually used by your code. The latter solution, whilst more work initially, will have the added benefit of much faster compilation times, and make your code more resistant to changes in subsequent releases of WIN32API.INC.


Error 407 - Source line too long

Source line too long - The line of code is too long for the compiler to process. This can also occur if the file contains lines of source code that are not CR/LF delimitered. Try breaking the line of code up into smaller logical lines with the use of line continuation characters, and ensure that the file is using the Win32 standard of CR/LF line delimiting. If you are using a 3rd-party editor, try opening the source code file in the PowerBASIC IDE and examine the lines where the error occurred -- merged lines here will be a good indication of invalid line delimiting.


Error 408 - Wrong compiler for this program

Wrong compiler for this program - The compiler you are using is not compatible with the compiler version specified by the #COMPILER metastatement. Use the compiler specified by the #COMPILER metastatement. Another approach would be to change the #COMPILER settings to match your compiler but, this should be done with caution, since the program may no longer work the same way (or at all) with a different compiler.


Error 409 - Sub/Function is too large

Sub/Function is too large - The number of lines of code Sub or Function is too long for the compiler to process. This can occur if the Sub or Function contains more than 12,000 lines of code. Break the Sub or Function down into two or more Subs or Functions.


Error 411 - "," expected

"," expected - The statement's syntax requires a comma (,).


Error 412 - ";" expected

";" expected - The statement's syntax requires a semicolon (;).


Error 413 - "(" expected

"(" expected - The statement's syntax requires a left parenthesis (().


Error 414 - ")" expected

")" expected - The statement's syntax requires a right parenthesis ()). The compiler encountered text or symbols where a right parenthesis was expected, or a parenthesis is missing. This error can also occur when attempting to pass more than 32 parameters to a Sub or Function.


Error 415 - "=" expected

"="expected - The statement's syntax requires an equal sign (=).


Error 416 - "-" expected

"-" expected - The statement's syntax requires a hyphen (-).


Error 417 - "*" expected

"*" expected - The statement's syntax requires an asterisk (*).


Error 418 - Statement expected

Statement expected - A PowerBASIC statement was expected. Some character could not be identified as a statement, metastatement, or variable.


Error 419 - Label/line number expected

Label/line number expected - A valid label or line-number reference was expected in an , GOTO, GOSUB, or statement.


Error 420 - Relational operator expected

Relational operator expected - The compiler has found a operand in a position where a operand should be, or a type mismatch has been detected.

For example, the statement X& = Y$ triggers an error because a string cannot be assigned or compared to numeric variable, hence the compiler expected to find an additional operator that would return a numeric result. For example, X& = Y$ > Z$.


Error 421 - String operand expected

String operand expected - The compiler expected a string expression and found something else; for example, X$ = A$ + 3.


Error 422 - Scalar variable expected

Scalar variable expected - The compiler expected a scalar variable as a formal parameter to a user-defined function. Scalar variables are non-array variables.


Error 423 - Array variable expected

Array variable expected - An array variable was expected in a DIM statement.


Error 424 - Numeric variable expected

Numeric variable expected - A variable was expected, such as in an INCR or DECR.


Error 425 - String variable expected

String variable expected - A variable was expected, such as in a PUT$ or a GET$ statement.


Error 426 - Variable expected

Variable expected - A variable was expected, but not found. A common cause for this error is the use of a reserved keyword as a variable.


Error 427 - Integer constant expected

Integer constant expected - An constant, numeric literal, or numeric equate was expected, such as in a named constant assignment.

This error can occur when attempting to use a numeric variable to dictate the size of the target of a fixed-length or ASCIIZ string pointer. For example:

DIM X AS STRING PTR * Y&

…is not permitted as this statement could only be evaluated at run-time. However:

DIM X AS STRING PTR * 1024

…is acceptable as the target size is known at compile-time.

Another cause of this error is specifying a non-integer CASE argument in a SELECT CASE AS CONST block.


Error 428 - Positive integer constant expected

Positive integer constant expected - A positive constant was expected, but not found.


Error 429 - String constant expected

String constant expected - A string constant was expected, but not found. For example, this error can occur when in a SELECT CASE AS CONST$ block when a non-string CASE argument is specified.


Error 430 - Integer variable expected

Integer variable expected - An variable was expected, but not found


Error 431 - Numeric scalar variable expected

Numeric scalar variable expected - The counter variable in a FOR/NEXT counter variable is a parameter passed to the Sub/Function, a target, a THREADED variable, an array variable (non-scalar), or the counter variable is not a data type. Scalar variables are non-array variables.


Error 432 - Long-integer variable expected

Long-integer variable expected - A Long-integer variable is expected.


Error 433 - Matrix array expected (integer/float)

Matrix array expected (integer/float) - Matrix arrays may only be of or types.

See Also

MAT Statement


Error 434 - End of line expected

End of line expected - No characters are allowed on a line (except for a comment) following a metastatement, END SUB, or a label.


Error 435 - #IF expected

#IF expected - An #ENDIF conditional compilation metastatement is missing its accompanying #IF. Look for all #ENDIF metastatements and figure out where to put the associated #IF.


Error 436 - #ENDIF expected

#ENDIF expected - An #IF conditional compilation metastatement is missing its accompanying #ENDIF. Examine all #IF metastatements to determine where to put the associated #ENDIF.


Error 437 - AS expected

AS expected - The AS reserved word is missing, such as in a variable declaration.


Error 438 - Member name expected

Member name expected - The compiler encountered a statement or other text where a structure member name was expected.


Error 439 - GOSUB expected

GOSUB expected - An ON statement is missing its accompanying GOSUB part.


Error 440 - GOTO expected

GOTO expected - An ON statement is missing its accompanying GOTO part.


Error 441 - IN expected

IN expected - The IN reserved word is missing in a REGEXPR, REGREPL, or REPLACE statement. Check the syntax of the relevant statement in the reference directory section.


Error 442 - THEN expected

THEN expected - An is missing its accompanying THEN part.


Error 443 - TO expected

TO expected - Missing TO in a FOR statement. This can also be reported for a missing TO in the CALL FuncName TO syntax.


Error 444 - WITH expected

WITH expected - The WITH reserved word is missing in a REPLACE statement.


Error 445 - OF expected

OF expected - Indexed with dual indexes require an "OF Limit" clause on both indexes. For example:

x = @w[i& OF m&, j& OF n&]


Error 446 - FUNCTION expected

FUNCTION expected - The compiler found an END FUNCTION or EXIT FUNCTION statement without a FUNCTION defined. When defining a FUNCTION, it must begin with a FUNCTION statement.


Error 447 - IF expected

IF expected - The compiler found an END IF or an EXIT IF statement without a beginning IF statement defined.


Error 448 - DO loop expected

DO loop expected - The compiler found a LOOP or EXIT LOOP statement without a beginning DO statement defined.


Error 449 - SELECT expected

SELECT expected - When defining a SELECT CASE statement, you either forgot to include the reserved word SELECT or the compiler ran into an END SELECT or EXIT SELECT without a beginning SELECT CASE statement. This error can also occur if you try to use the reserved word CASE as a variable name in your program.


Error 450 - CASE expected

CASE expected - When defining a SELECT CASE statement, you forgot to include the reserved word CASE. This error can also occur if you try to use the reserved word SELECT as a variable name in your program.


Error 451 - FOR loop expected

FOR loop expected - The compiler found an EXIT FOR statement without a beginning FOR statement defined.


Error 452 - SUB expected

SUB expected - The compiler found an END SUB or EXIT SUB statement without a procedure defined. You must define a procedure by beginning it with a SUB statement.


Error 453 - Equate (%xyz) expected

Equate (%xyz) expected - %DEF may only be used with an equate statement. Example: %DEF(%xyz) returns TRUE (non-zero) or FALSE (zero) as to whether equate "%xyz" has been defined or not.


Error 454 - END FUNCTION expected

END FUNCTION expected - A FUNCTION block was not terminated with a corresponding END FUNCTION statement; or another block terminating statement (for example, WEND or LOOP) was encountered when an END FUNCTION statement was expected.


Error 455 - END IF expected

END IF expected - An IF block was not terminated with a corresponding END IF statement.


Error 456 - LOOP/WEND expected

LOOP/WEND expected - A DO or WHILE loop was not terminated with a corresponding LOOP or WEND statement.


Error 457 - END SELECT expected

END SELECT expected - A SELECT CASE statement was not properly terminated with an END SELECT statement.


Error 458 - END SUB expected

END SUB expected - A procedure was not properly terminated with an END SUB statement, or another block terminating statement (for example, WEND or LOOP) was encountered when an END SUB statement was expected.


Error 459 - NEXT expected

NEXT expected - A FOR loop was not properly terminated with a NEXT statement.


Error 460 - Undefined equate

Undefined equate - A named constant was used in the program without it being defined. Explicitly define the named constant or use a literal constant in the statement instead.


Error 461 - Array not dimensioned

Array not dimensioned - The array being referenced has not yet been dimensioned. The UBOUND and ARRAYATTR functions can be used to test for an undimensioned array without generating an error.


Error 462 - Undefined SUB/FUNCTION reference

Undefined SUB/FUNCTION reference - You attempted to use CALL to a Sub procedure, or referenced a Function, but you have not defined the target Sub/Function. This error can also occur if you attempted to use THREAD CREATE with an imported function. Check the name of the Sub/Function for typographical errors or misspellings, or define the Sub/Function.


Error 463 - Undefined label/line reference

Undefined label/line reference - You used a line number or label in an IF, GOTO, GOSUB, or statement, but you did not define the label or line number. Check the label or line number for typos or provide the label if it does not exist. Labels are always local to the Sub or Function in which they are defined.


Error 465 - Duplicate definition

Duplicate definition - A program element which should only appear once was duplicated in your code. For example, a duplicated #STACK statement could cause this error. A common source of this problem is nested or multiple #INCLUDE files that define the same item.

See Also

Error 466 - Duplicate name definition


Error 466 - Duplicate name definition

Duplicate name definition - A SUB name, FUNCTION name, label name, or variable name was defined more than once in your code.

Check your program and any Include files for duplicate names and change one or both of them. A common source of this problem is nested or multiple #INCLUDE files that define the same item.

The compiler always highlights the second incidence of the duplicate name, so a common strategy to quickly locate the first incidence is to move the second incidence as close to the top of the source code as possible. This effectively reverses the relative positions of first and second incidences, so the next compile-time error will identify what was (originally) the first incidence.

A good technique for dealing with multiple #INCLUDE files is to use conditional compilation blocks to exclude duplicate definitions. For example:

#IF NOT %DEF(%FILESPEC_INC)

%FILESPEC_INC = -1

'...the body of the #INCLUDE file goes here

#ENDIF ' %FILESPEC_INC

...where %FILESPEC_INC is based on the name of the #INCLUDE file or some other distinctive equate. During compilation, the first incidence will be included in the compiled code (because the equate will not be defined up to that point), but additional incidences will be automatically excluded once the equate is defined.

The WIN32API.INC and related files use this technique extensively.


Error 467 - Duplicate line number

Duplicate line number - The same line number was used twice. Check your program and any include files for duplicate line numbers and change one or both of them.

See Also

Error 466 - Duplicate name definition


Error 468 - Duplicate equate

Duplicate equate - Two equates with the same name and different values was encountered. PowerBASIC permits the duplicate definitions of both numeric and string equates, but only if the equate content is identical across all definitions. You cannot redefine the value for an existing equate. A common source of this problem is nested #INCLUDE files that define the same item.

See Also

Error 466 - Duplicate name definition


Error 471 - Invalid line number

Invalid line number - Line numbers must be in the range 1 through 65535.


Error 472 - Invalid label

Invalid label - A label in your code contains invalid characters, such as the period character.


Error 473 - Invalid numeric format

Invalid numeric format - Your program declared a with more than 18 digits or a number with an E component without the exponent value. This error can also occur if the "&" concatenation operator is used without leading whitespace. For example: a$ = a$&b$ should be written a$ = a$ & b$

p;
p;


Error 474 - Invalid name

Invalid name - A function, procedure, macro, or label has an invalid name. In the case of a Sub or Function, the name must begin with a letter and can be followed by other letters, digits, and underscores, but may not include a type-specifier or period. In the case of a macro you may have a duplicate macro name defined.


Error 475 - Metastatements not allowed here

Metastatements not allowed here - A metastatement must be the first statement on a line.


Error 476 - Block/scanned statements not allowed here

Block/scanned statements not allowed here - Block statements (like WHILE/WEND, DO/LOOP, and SELECT CASE) are not allowed in single line IF statements. In addition, you may not have a Sub or Function definition nested within the body of another definition. A missing END SUB or END FUNCTION can also cause this error.


Error 477 - Syntax error

Syntax error - Something is incorrect on the line; however, the compiler could not determine a proper error message or decode the line further. A common cause is mixing two statement keywords together, using a reserved keyword for a variable name, or attempting to use an undefined COM INTERFACE member (in an OBJECT statement) when using early-binding, etc.


Error 478 - Resource file error

Resource file error - The resource file referenced has not been found or is not identifiable as valid resource file. A common cause of this problem is attempting to use #RESOURCE with a non-PBR file, or if the PBR file was not able to be opened by the compiler (for example, because the file is locked by another process or application).


Error 479 - Array bounds error

Array bounds error - You dimensioned an array within a User-Defined Type that contains invalid array boundaries. For example:

TYPE MyType

ArrayWithinUDT(5 TO 1)

...


Error 480 - Parameter mismatches definition

Parameter mismatches definition - A parameter used in a call to a does not match (or cannot be converted to) the data type or reference type ( , , ) found in the procedure declaration/definition.

This error commonly occurs when attempting to pass the wrong type of variable as a parameter, or parameter order may be incorrect. Often, passing the parameter BYCOPY may resolve the error in a legitimate manner. See CALL for more information.

Other causes include: passing too few or too many parameters to a Sub/Function, omitting a comma between parameters, and by the inclusion of a space or other illegal character in the middle of a parameter name, etc.

In some cases, it may be permissible to override the compilers type-checking by passing a to the variable BYVAL, rather than using BYCOPY. For example:

CALL MySub(BYVAL VARPTR(variable))


Error 481 - Mismatch with prior definition

Mismatch with prior definition - A program element (UDT, Union, Function, Macro, etc.) implementation mismatches a previous definition. It could be a Function which mismatches a declaration, a duplicate INTERFACE/UDT/etc. which is not identical, or another similar condition.

Check your program and any #INCLUDE files for duplicate names and change one or both of them. A common source of this problem is nested or multiple #include files that define the same item.

The compiler always highlights the second incidence of the duplicate name, so a common strategy to quickly locate the first incidence is to move the second incidence as close to the top of the source code as possible. This effectively reverses the relative positions of first and second incidences, so the next compile-time error will identify what was (originally) the first incidence.

A good technique for dealing with multiple #INCLUDE files is to use conditional compilation blocks to exclude duplicate definitions. For example:

#IF NOT %DEF(%FILESPEC_INC)

%FILESPEC_INC = -1

'...the body of the #INCLUDE file goes here

#ENDIF ' %FILESPEC_INC

...where %FILESPEC_INC is based on the name of the #INCLUDE file or some other distinctive equate. During compilation, the first incidence will be included in the compiled code (because the equate will not be defined up to that point), but additional incidences will be automatically excluded once the equate is defined.

The WIN32API.INC and related files use this technique extensively.


Error 482 - Data type mismatch

Data type mismatch - There is a previous declaration with a parameter mismatch.


Error 484 - Requires procedure (Sub/Function)

Requires procedure (Sub/Function) - LOCAL and STATIC variables may only be declared within a Sub or Function block.


Error 485 - Dynamic/Field strings not allowed

Dynamic/Field strings not allowed - Fixed-length strings and ASCIIZ are the only that can be used in User-Defined Types. Use the AS STRING * %X or AS ASCIIZ * %X type declaration (where %X is a numeric literal or a numeric equate).


Error 486 - BYVAL option not allowed

BYVAL option not allowed - The option cannot be used here.


Error 487 - Name Declaration Error

Name Declaration Error - An attempt was made to use a Function referenced ambiguously prior to its declaration; or a name was used prior to an exclusive declaration being encountered. All Functions must either be declared or physically positioned in the source code above the reference before being referenced.


Error 488 - Numeric processor overflow

Numeric processor overflow - This operation requires more than the 8 floating-point registers that are available in the FPU. Simplify the operation by dividing complex expressions into multiple separate lines of code, and/or try adding the metastatement: #REGISTER NONE into the affected Sub or Function to increase the number of general floating-point registers available. The latter solution may impact execution speed if floating-point Register variables are used extensively in the Sub/Function.


Error 489 - Invalid string length

Invalid string length - You attempted to DIM a fixed-length string with a length of zero, or you attempted to create a string equate whose length exceeds 255 characters. Fixed-length strings must be at least 1 byte long, and individual string equates may not exceed 255 bytes in length


Error 490 - Static array too large

Static array too large - You attempted to dimension a static array larger than 16 MB in a User-Defined Type.


Error 491 - Invalid register variable

Invalid register variable - Register variables may only contain Integer, Word, Long-integer, Double-word, or Extended-precision floating-point variables. This error can also occur when attempting to use a register variable where a memory variable can only be used. For example, with VARPTR.

See Also

Optimizing your code


Error 493 - Compiler file not found/accessible

Compiler file not found/accessible - An #INCLUDE file could not be found in the specified directory path, or the current directory, or in the search path specified in the compiler /I command-line option. Alternatively, the file may be locked by another process. Check the directory paths or make sure that the specified file exists, and that another process or application has not locked the file.


Error 494 - ASM not allowed here

ASM not allowed here - You tried to use multiple statements on a line containing an ASM statement. An ASM statement must be the only statement on a line (plus an optional comment or REM statement).


Error 495 - Compiler file read error

Compiler file read error - During the compilation process, the compiler tried to open a #INCLUDE file, but a disk error was encountered. Check the file is not locked by another process or application, and that the disk is free from errors, for example, using SCANDISK.EXE, etc.


Error 496 - Destination file write error

Destination file write error - During compilation the compiler received a disk write error. This can occur if the destination EXE is, for example, still running in memory when you attempt to compile, the target file is write locked by another process or compile session, or the target file is write-protected (read-only).


Error 497 - Assembler syntax error

Assembler syntax error - An ASM statement contains an invalid assembly-language construction.


Error 498 - Assembler variables must be declared

Assembler variables must be declared - An attempt was made to reference an assembler variable before it was defined.


Error 499 - Statement must be first on line

Statement must be first on line - A statement, such as CASE, must be the first statement on a line. Split the compound statements apart so that each statement is on a separate line.


Error 500 - 32-bit operating system error

32-bit operating system error - An unexpected or unknown operating system error was encountered during the compile process. Try closing all applications and/or rebooting the computer and compiling again. In some cases of instability after an app or system crash, simply closing the PowerBASIC IDE and reopening it may also overcome the problem, but rebooting is recommended.


Error 501 - Parameters too large (exceed 64 Kb)

Parameters too large (exceed 64 Kb) - The specified parameters exceeded the 64 Kb limit for that parameter type. Reduce the size of the parameters as necessary to fit within the 64 Kb limit.


Error 503 - Multiple Main Functions have been defined

Multiple Main Functions have been defined - The source code contains more than one entry point function. To resolve this error, remove the surplus entry point function(s), leaving just one entry point function appropriate to the type of file being compiled.

Both PowerBASIC for Windows and the PowerBASIC Console Compiler directly support three types of entry point functions for executable files (MAIN, WINMAIN, PBMAIN). All executable programs must contain one of these functions so Windows can determine where program execution should begin.

Conversely, an entry point function is optional in code that produces a dynamic link library (.DLL). However, if an entry point function is required, PowerBASIC for Windows provides three types to choose from (DLLMAIN, LIBMAIN, and PBLIBMAIN).


Error 504 - Executable requires PBMAIN/WINMAIN function

Executable requires PBMAIN/WINMAIN function - No WINMAIN or PBMAIN function was located in an executable program. Without one of these functions, it is not possible for Windows to execute the program.


Error 505 - Debugging requires EXE file, not DLL

Debugging requires EXE file, not DLL - An attempt was made to launch the debugger on a DLL rather than an EXE file (PB/Win only). Be sure to use an explicit #COMPILE EXE metastatement to ensure the compiler generates the correct type of compiled code.


Error 506 - Declaration must precede statements

Declaration must precede statements - You attempted to use a declaration, such as a #DIM ALL metastatement after executable code. Move the declaration to a position before any statements that generate executable code.


Error 510 - Interface name expected

Interface name expected - The compiler encountered a statement or other text where an interface name was expected.


Error 511 - Numeric operand expected

Numeric operand expected - The compiler encountered a statement or other text where a numeric operand was expected.


Error 512 - Nested brackets not allowed

Nested brackets not allowed - You attempted to use nested brackets in an parameter declaration.


Error 513 - "]" expected

"]" expected - The statement's syntax requires a closing bracket ( ] ).


Error 514 - Enclosing <...> angle brackets expected

Enclosing <...> angle brackets expected - An INTERFACE definition block member item requires a parameter enclosed with angle brackets to identify the member ID.


Error 515 - Fixup overflow

Fixup overflow - You have a jump short instruction that exceeds its maximum length.


Error 516 - DEFtype, Type ID or type-specifier required

DEFtype, Type ID or type-specifier (?%&!#$), or AS ... required - A variable with no type declaration was found and no DEFtype statement (such as DEFINT) was found. The compiler was unable to identify the type of variable indicated. The misspelling of variable names commonly causes this error. The DEFtype statement may not be supported in future editions of PowerBASIC. Use explicit declarations wherever possible to maintain future compatibility.


Error 517 - OPTIONAL requires CDECL or SDECL

OPTIONAL requires CDECL or SDECL - The (or OPT) clause in a DECLARE, SUB or FUNCTION statement requires the or calling convention. You may not use OPTIONAL or OPT parameters with calling convention.


Error 518 - "[...]" requires CDECL

"[...]" requires CDECL - You attempted to use the classic parameter syntax in a SUB or FUNCTION without declaring it to use calling convention ('C' style calling convention).


Error 519 - Missing declaration

Missing declaration - You specified that variables and/or arrays should be declared before use, but didn't declare a variable or array. Use DIM, GLOBAL, LOCAL or STATIC to declare a variable's type or an array's dimensions. To declare Register Variables use the REGISTER statement.


Error 520 - TYPE expected

TYPE expected - The compiler found an END TYPE statement without a preceding TYPE statement.


Error 521 - UNION expected

UNION expected - The compiler found an END UNION statement without a preceding UNION statement.


Error 522 - END TYPE expected

END TYPE expected - The compiler found a TYPE statement without a terminating END TYPE statement.


Error 523 - END UNION expected

END UNION expected - The compiler found a UNION statement without a terminating END UNION statement.


Error 524 - Undefined type

Undefined type - You attempted to define or use a record or Union variable, but PowerBASIC could not find such a Type or member. Look for misspellings or missing definitions/declarations.


Error 525 - Type ID or specifier (?%&!#$) not allowed

Type ID or specifier (?%&!#$) not allowed - Members in a User-Defined Type (UDT)or UNION variable must not include type ID or type-specifier characters. Change the definition to use the AS type syntax instead.


Error 526 - Period not allowed

Period not allowed - You cannot include periods within the individual member names of User-Defined Type (UDT) or UNION variables. Nor may period characters be used in scalar variable names. Underscores are permitted, and are the preferred way to decorate variable names. For example:

DIM Customer_Counter&

An error 526 can also occur in an expanded macro if the resultant code is not syntactically correct due to whitespace in the passed macro parameter.


Error 527 - End of statement expected

End of statement expected - There was an extra character at the end of a statement or line of source code.


Error 528 - Type too large

Type too large - You attempted to define a User-Defined Type (UDT) or UNION that exceeded 16 Megabytes. An array, ASCIIZ string or fixed-length string member of a UDT or UNION exceeded the 16 MB structure size limit.

Individual members of a UDT may be up to 16 MB in size, but the total size of all members must not exceed 16 MB. Union members may each be up to 16 MB, since they are not in a sequential memory block.

This error can also occur if additional padding (due to the alignment specification for the structure, such as WORD, DWORD, FILL, etc) takes the storage requirement past the 16 MB limit.


Error 529 - Pointer variable error

Pointer variable error - You attempted to use a variable incorrectly, such as using the leading at-sign (@) with a variable which is not a pointer.


Error 530 - Invalid member name/definition

Invalid member name/definition - This covers a variety of errors involving members of User-Defined Types (UDTs) or UNIONS, and INTERFACES. For example, a structure with no members, an invalid name, or an attempt to embed a disallowed (such as a Variant) might cause this error. In addition, a member of a UDT cannot use the same name as the UDT itself. PowerBASIC does not support this form of circular reference.


Error 531 - Object variable expected

Object variable expected - You used a non-object variable in a COM statement. Object variables are used to enable communication with the exposed interfaces of COM objects.

Check the variable name or change the declaration of the variable to an object variable of the correct type, for example:

INTERFACE DISPATCH WordApplication

...

DIM oWord1 AS WordApplication

DIM oWord2 AS DISPATCH


Error 532 - Variant variable expected

Variant variable expected - You used a variable in a COM statement, where the variable was not declared as a VARIANT data type. Check the variable name or change the declaration of the variable to use the VARIANT type, for example:

DIM vVnt AS VARIANT


Error 533 - Dispatch object variable expected

Dispatch object variable expected - You used a variable that was not declared as a DISPATCH type in a COM statement. DISPATCH object variables are used with late-binding of COM objects. Check the variable name or change the declaration of the variable to use the DISPATCH type, for example:

DIM objVar AS DISPATCH


Error 534 - Bit field error

Bit field error - An error was made in defining a bit field of BIT/SBIT variables. For example, it could be that the first variable in the bit field did not define the total size (using IN BYTE|WORD|DWORD), or the total number of bits may have exceeded the maximum of 32.


Error 535 - Dynamic string variable expected

Dynamic string variable expected - A dynamic string variable was expected, such as when a requires a dynamic string by reference and you try to pass a fixed-length or ASCIIZ string.


Error 536 - Too many imports

Too many imports - The program has exceeded the maximum number of allowed imports.


Error 537 - Pointer expected

Pointer expected - This operation expects a . For example:

... @PtrName[Min OF Max]


Error 538 - Invalid FOR/NEXT limits

Invalid FOR/NEXT limits - The specified start, stop and/or increment value(s) for a FOR/NEXT loop are not within the allowable range for the class of counter variable used. For example, you attempted to specify an increment value of -1 (a signed value) when the loop counter uses an unsigned variable. This error is also generated if the compiler is able to determine, at compile time, that the start and stop values chosen will prevent the FOR/NEXT from ever executing, e.g., FOR x = 10 TO 1.


Error 539 - Invalid thread function

Invalid thread function - A valid thread Function may only take one 32-bit LONG or DWORD parameter, which must be received by value ( ). This error can occur if the thread Function does not match the following syntax:

FUNCTION ThreadFuncName (BYVAL param AS {LONG | DWORD}) AS {LONG | DWORD}

An error 539 can also occur if the target thread Function is declared to use a DWORD parameter but is passed a Long-integer, or vice-versa. You must pass the correct (matching) for the thread Function parameter. For example:

THREAD CREATE MyThread(y&) TO hThread???

...

FUNCTION MyThread(BYVAL x AS LONG) AS LONG

Or

THREAD CREATE MyThread(y???) TO hThread???

...

FUNCTION MyThread(BYVAL x AS DWORD) AS LONG

See Also

THREAD CREATE statement


Error 540 - Float opcode with a register variable

Float opcode with a register variable - An attempt was made to perform a operation with an register variable.

See Also

#REGISTER metastatement


Error 541 - Register size conflict

Register size conflict - An attempt was made to use a conflicting size variable or register within an Inline Assembler statement. For example, attempting to load a register with a differing size register:

ASM MOV AX, EAX

ASM MOVZX EAX, EAX

ASM ADD AL, ESI


Error 542 - May not be altered

May not be altered - An attempt was made to change the value of a read-only parameter. For example, COMM SET cannot be used with RING, RLSD, RXQUE or TXQUE.


Error 543 - Must be outside Sub/Function

Must be outside Sub/Function - A statement or metastatement was incorrectly placed within a Sub or Function block. Move the offending code outside of the Sub or Function block.


Error 544 - Field variable expected

Field variable expected - The compiler encountered a statement or other text where a FIELD variable was expected.


Error 545 - AT expected

AT expected - A required AT keyword is missing from the statement indicated.


Error 546 - Use only as a Callback

Use only as a Callback - An attempt was made to explicitly execute a DDT Callback Function. (PB/Win only)

DDT Callback Functions may not be directly invoked except by the DDT engine and Windows itself.

To invoke a Callback Function indirectly, send an appropriate window message to the callback's related dialog or control using CONTROL SEND or DIALOG SEND, etc.

To send custom messages, be sure to use message values that are at least %WM_USER + 500& in value to avoid conflicts with common-control notification messages. To obtain a unique message number, use the RegisterWindowMessage API function.


Error 547 - Callback function required

Callback function required - A Callback Function was named but the target function was not defined as a CALLBACK, or the nominated function was not a Callback Function. (PB/Win only)


Error 548 - No parameters with Callback

No parameters with Callback - A Callback Function definition cannot specify parameters. (PB/Win only)

Omit the parameters from the function definition. For example:

CALLBACK FUNCTION Dlg1Callback()

...

END FUNCTION


Error 549 - BYVAL required with pointers

BYVAL required with pointers - may only be passed . Add an explicit BYVAL to the Sub/Function declaration and prototype. Previous versions of PowerBASIC used an implied BYVAL.


Error 550 - Too many data statements

Too many data statements - Data is limited to 64 Kb per Sub or Function, and 16384 individual data items. Either reduce the number of DATA statements, or split the data into separate Subs/Functions.


Error 551 - Not supported in this version

Not supported in this version - An attempt was made to use a feature that is not supported by this version of the compiler. This error may also occur if a reserved word is used as a variable, label, Sub, or Function name. For example, using INP or OUT.


Error 552 - TRY statement expected

TRY statement expected - PowerBASIC expected to find a TRY statement at or before the indicated position in the code. Check the syntax of the surrounding code for other syntax errors, such as the misplacement of a CATCH or END TRY statement, conditional compilation excluding required portions of the code, etc.


Error 553 - CATCH statement expected

CATCH statement expected - A TRY/END TRY block did not include a CATCH statement. Recheck the syntax of the block.


Error 554 - END TRY statement expected

END TRY statement expected - A TRY/END TRY block appears to be missing its END TRY clause. This can typically occur if an END SUB or END FUNCTION statement was encountered within the TRY/END TRY block.


Error 555 - ON ERROR/RESUME not allowed here

ON ERROR/RESUME not allowed here - An attempt was made to include an ON ERROR or a RESUME statement inside a TRY/END TRY block. Remove the ON ERROR or RESUME statement or move it out of the TRY/END TRY block. Error handling is automatic within a TRY/END TRY block.


Error 556 - Function restricted to threads

Function restricted to threads - Functions that are called with THREAD CREATE may not be called in the conventional manner. This restriction is necessary because thread Functions require additional initialization steps that are not included in standard function code.

One situation that can arise is where a Function may need to be invoked both directly and used as a thread Function. The easiest solution is to create a small wrapper function for the function, then use THREAD CREATE with the wrapper function, or call the original function directly. For example:

FUNCTION WorkerFunc(BYVAL x AS LONG) AS LONG

' code here

END FUNCTION

FUNCTION WorkerThread(BYVAL x AS LONG) AS LONG

FUNCTION = WorkerFunc(x)

END FUNCTION

...

' Execute the worker function directly, thus:

lResult& = WorkerFunc(var&)

' Execute the worker thread as a thread, using

' the wrapper function:

THREAD CREATE WorkerThread(var&) TO hThread???


Error 557 - Macro too long/complex

Macro too long/complex - An attempt was made to create a MACRO that is too long or complex. An individual macro can contain replacement text of up to approximately 4000 characters, and can specify up to 240 parameters occupy up to approximately 2000 bytes expanded space per macro. Macro substitutions are limited to an expanded total of approximately 16000 characters per line of original source code.


Error 558 - MACRO expected

MACRO expected - An END MACRO statement was found without a matching MACRO statement. Please recheck the syntax of the macro block.


Error 559 - END MACRO expected

END MACRO expected - A MACRO block appears to be missing a terminating END MACRO statement. Please recheck the syntax of the macro block.


Error 562 - INTERFACE expected

INTERFACE expected - An END INTERFACE statement was found to be without a matching INTERFACE statement. Please recheck the syntax of the interface definition block.


Error 563 - END INTERFACE expected

END INTERFACE expected - An INTERFACE statement was found without a matching END INTERFACE statement. Please recheck the syntax of the interface definition block.


Error 564 - MACROTEMP not allowed here

MACROTEMP not allowed here - PowerBASIC encountered a MACROTEMP statement outside the scope of a MACRO block.


Error 565 - Macro mismatch with code position

Macro mismatch with code position - The compiler encountered a multi-line MACRO statement in a non-statement position.


Error 601 to 615 - Internal error

Internal error - If one of these errors occurs, please report it to the PowerBASIC Technical Support group.


Run-time errors

Run-time errors are generated when execution of a particular code statement or function results in an error condition being set. Run-time errors caught by PowerBASIC include Disk access violations (i.e., trying to write data to a full disk), out of bounds array and pointer access, and Memory allocation failures. Array bounds and null-pointer checking is only performed when #DEBUG ERROR ON is used.

Run-time errors can be trapped; that is, you can cause a designated error-handling subroutine to get control should an error occur. Use the ON ERROR statement to accomplish this. This routine can "judge" what to do next based on the type of error that occurs. File-system errors (for example, disk full) in particular are prime candidates for run-time error-handling routines. They are the only errors that a thoroughly debugged program should have to deal with.

The ERROR statement (which simulates run-time errors) can be used to debug your error-handling routines. It allows you to deliberately cause an error condition to be flagged. Avoid using error numbers higher than 240, as they are reserved for use in critical error situations which can never be trapped with ON ERROR.

In the situation where an undocumented run-time error occurs, the chief suspect is memory corruption. This can typically be cause by writing beyond an array boundary, improper use of pointers, bad Inline Assembly code, etc.

See Also

Error Overview

Compile-time errors

Disk Errors

Error Trapping


Disk Errors

Disk and I/O errors are always trapped at run-time by PowerBASIC. If a run-time Disk or I/O error is detected, the error code is placed in the ERR system variable. If ON ERROR is enabled, code execution will branch to the designated local error handler.

All error handling in PowerBASIC is local to each Sub or Function. You cannot create a global error handler routine as you can in some DOS BASICs.

When an error occurs in PowerBASIC, an error code is placed into the ERR system variable. If Error Trapping has been enabled, execution branches to the error trap. Otherwise, execution continues. If an error occurs and your code does not take care of it, either by using an error trap or by explicitly testing the ERR or ERRCLEAR variables, your program may produce unpredictable results. For example, in the following code, several problems can occur which would cause the code to fail, and possibly even trigger a General Protection Fault (GPF) in Windows:

SUB ReadFile(Filnam$, buffer$(), Lines%)

RESET Lines%

OPEN Filnam$ FOR INPUT AS #1

WHILE ISFALSE EOF(1)

INCR Lines%

LINE INPUT #1, buffer$(Lines%)

WEND

CLOSE #1

END SUB

Here, the ERR variable is not checked after the OPEN statement to see if it was successful. If the file does not exist or has been locked by another process, a run-time error can occur. In this case, EOF(1) will never be able to return TRUE (non-zero) since the file was not able to be opened, and therefore the end of the file cannot be determined. Further, checking the EOF of a file that has not been opened will trigger yet another run-time error.

The result is that without adequate error testing, this small loop will begin to run continuously.

While certainly a flaw in the code, no harm will come to the program for period. However, a fatal error in the LINE INPUT# statement is imminent if the Lines% variable value exceeds the UBOUND of the buffer$() array. A fatal error could also occur if buffer$() was not previously dimensioned, or it wasn't dimensioned with enough elements to store the entire file (that is, assuming the file was opened successfully).

In these cases, a General Protection Fault (GPF) is quite likely to occur, as soon as invalid memory addresses begin to be accessed in an attempt to store the string data. You can prevent the array boundary GPF by turning on error checking using the #DEBUG ERROR ON metastatement. However, if the array was not previously dimensioned or does not have enough space, the code will still fail in its overall objective.

A more robust version of this example code follows:

#DEBUG ERROR ON

SUB ReadFile(Filnam$, buffer$(), Lines%)

LOCAL Temp$

ON ERROR RESUME NEXT

RESET Lines%

OPEN Filnam$ FOR INPUT AS #1

IF ERR THEN 'error opening file

EXIT SUB

END IF

WHILE ISFALSE EOF(1)

INCR Lines%

LINE INPUT #1, Temp$

IF ERR then EXIT SUB 'abort if disk error

buffer$(Lines%) = Temp$

IF ERR = 9 THEN 'subscript out of range

REDIM PRESERVE buffer$(Lines%) 'increase array size

buffer$(Lines%) = Temp$

END IF

WEND

CLOSE #1

END SUB

See Also

Error Trapping

Error Overview

Compile-time errors

Run-time errors


Error 0 - No error

No error (%ERR_NOERROR)


Error 5 - Illegal function call

Illegal function call - (%ERR_ILLEGALFUNCTIONCALL) - This is a catch-all error related to passing an inappropriate argument to some statement or function.

There are many things that can cause an Error 5, for example:

· Trying to set TIME$ to an invalid value.

· A record number is too large (or negative) in a GET or PUT.

· Attempting to use the WIDTH# statement on a .

· The run-time execution of a LET or OBJECT statement failed (see OBJRESULT to obtain an extended error code).


Error 6 - Overflow

Overflow (%ERR_OVERFLOW) - This error is not currently supported.


Error 7 - Out of memory

Out of memory - (%ERR_OUTOFMEMORY) - Many different situations can cause this message, including dimensioning too large an array, or running out of virtual memory due to insufficient free disk space for the Windows swap file.


Error 9 - Subscript / Pointer out of range

Subscript / Pointer out of range - (%ERR_SUBSCRIPTPOINTEROUTOFRANGE) - You attempted to use a subscript smaller than the minimum or larger than the maximum value established when the array was dimensioned. Attempting to use a null or invalid may also cause this error. Error 9 will only be generated if you have specified #DEBUG ERROR ON.


Error 11 - Division by zero

Division by zero (%ERR_DIVISIONBYZERO) - This error is not currently supported.


Error 24 - Device time-out

Device time-out - (%ERR_DEVICETIMEOUT) - The specified time-out value for a UDP or TCP communications operation has expired.


Error 51 - Internal error

Internal error - (%ERR_INTERNALERROR) - A malfunction occurred within the PowerBASIC run-time system, or the operating system reported an error that PowerBASIC was not expecting (or was unable to decipher). For example, attempting to KILL (delete) an open file can cause this kind of problem.

If you are unable to identify the cause of the problem, contact the PowerBASIC Technical Support group with information about your program.


Error 52 - Bad file name or number

Bad file name or number - (%ERR_BADFILENAMEORNUMBER) - The file number you gave in a file statement does not match the file number given in an OPEN statement, or the file number may be out of the range of valid file numbers.


Error 53 - File not found

File not found - (%ERR_FILENOTFOUND) - The file name specified could not be found on the indicated drive.


Error 54 - Bad file mode

Bad file mode - (%ERR_BADFILEMODE) - You attempted a PUT or a GET (or PUT$ or GET$) on a file opened in sequential mode.


Error 55 - File is already open

File is already open - (%ERR_FILEISOPEN) - You attempted to OPEN a file that was already open, or you attempted to delete an open file.


Error 57 - Device I/O error

Device I/O error - (%ERR_DEVICEIOERROR) - A hardware problem occurred when trying to carry out some device-orientated command.

For example, a COMM connection was lost during a session, or a TCP/UDP statement failed to be connected, etc. Alternatively, a TCP/UDP port may have been closed unexpectedly or the network refused the connection requested.

If an ERROR 57 occurs with a TCP OPEN statement under Windows 98 when using a dotted IP address string (i.e., "202.123.456.1"), then check to ensure that "Client for Microsoft Networks" is installed in the Network applet in Control Panel. Alternatively, manually add a DNS entry in the HOSTS file in the \WINDOWS folder.

For example, add the following line into the HOSTS file, and change the TCP OPEN statement to use the (dummy) domain name instead of the dotted IP address:

202.123.456.1 dummyname.com


Error 58 - File already exists

File already exists - (%ERR_FILEALREADYEXISTS) - The new name argument specified in your NAME statement already exists.


Error 61 - Disk full

Disk full - (%ERR_DISKFULL) - There is not enough free space on the indicated or default disk to carry out a file operation. Create more free disk space and retry your program.


Error 62 - Input past end

Input past end - (%ERR_INPUTPASTEND) - You tried to read more data from a file than it had to read. Use the EOF (end of file) function to avoid this problem. Trying to read from a sequential file opened for output or append can also cause this kind of error.


Error 63 - Bad record number

Bad record number - (%ERR_BADRECORDNUMBER) - A number less than the BASE option specified in the OPEN statement or a number larger than 2^63-1 was specified as the record argument to a random file PUT or a GET statement.


Error 64 - Bad file name

Bad file name - (%ERR_BADFILENAME) - The file name specified in a KILL or NAME statement contains invalid characters.


Error 67 - Too many files

Too many files - (%ERR_TOOMANYFILES) - This error can be caused either by trying to create too many files in a drive's root directory, or by an invalid file name that affects the performance of the Create File system call.


Error 68 - Device unavailable

Device unavailable - (%ERR_DEVICEUNAVAILABLE) - You tried to a device or to a device or graphic without that device present or installed.
For example, opening COM1 on a system without a serial adapter or modem, or attempting to use TCP/IP or UDP/IP on a machine without Winsock 2.0 (or better) installed. Also, trying to attach to a graphic or printer that is not available will cause this error.


Error 69 - COMM error

COMM error - (%ERR_COMMERROR) - A communications error occurred. For example, a framing error may have occurred.


Error 70 - Permission denied

Permission denied - (%ERR_PERMISSIONDENIED) - You tried to write to a write-protected disk. This error can also be generated as a result of network permission errors, such as accessing a locked file, or a locked record. It can also occur when attempting to open a subdirectory as a file.


Error 71 - Disk not ready

Disk not ready - (%ERR_DISKNOTREADY) - The door of a floppy disk drive is open, or there is no disk in the indicated drive.


Error 72 - Disk media error

Disk media error - (%ERR_DISKMEDIAERROR) - The controller board of a floppy or hard disk indicates a hard media error in one or more sectors.


Error 74 - Rename across disks

Rename across disks - (%ERR_RENAMEACROSSDISKS) - You cannot rename a directory across disk drives or partitions.


Error 75 - Path/file access error

Path/file access error - (%ERR_PATHFILEACCESSERROR) - During a command capable of specifying a path name (OPEN, NAME, or MKDIR, for example), a path was used inappropriately. For example, attempting to delete a directory that is in-use.


Error 76 - Path not found

Path not found - (%ERR_PATHNOTFOUND) - The path you specified during a CHDIR, MKDIR, OPEN, etc, cannot be found.


Error 99 - Object error

Object error - (%ERR_OBJECTERROR) - A run-time error occurred involving an object.


Error 241 - Global memory corrupt

Global memory corrupt - (%ERR_GLOBALMEMORYCORRUPT) - PowerBASIC detected a global memory corruption.

Typical causes include misuse of , accessing an array beyond its boundary, or bad Inline Assembly code. The cause of the problem may actually be in a seemingly unrelated portion of the program, and/or in a DLL or module used by the program.

Error 241 was formerly deemed "Far heap corrupt" (%ERR_FARHEAPCORRUPT). While this equate remains supported for a short period, source code should be updated to maintain compatibility with future versions of PowerBASIC.


Error 242 - String space corrupt

String space corrupt - (%ERR_STRINGSPACECORRUPT) - PowerBASIC detected a memory or space corruption. Typical causes include misuse of , accessing an array beyond its boundary, or bad Inline Assembly code. The cause of the problem may actually be located in a seemingly unrelated portion of the program, and/or in a DLL or module used by the program.


Error Trapping

Error traps let you intercept and deal with run-time errors, rather than having programs unceremoniously abort or ignore a fatal error, possibly causing loss of data.

There are three steps you must take to trap errors, as described in the following sections:

1. Set the error trap. Use the ON ERROR GOTO statement.

2. Write the error-handling routine. The error-handling routine receives control when an error occurs.

3. Exit the error-handling routine. You exit the error handler using the RESUME statement so execution can continue at an appropriate location in the code.

For example, here is a piece of code that fills an array with the filenames from a directory. This section will add complete Error Trapping to prevent run-time errors when the user chooses a directory that does not have any files or a drive that is not ready.

SUB GetFileNames(File() AS STRING)

DIM CurrentDir AS STRING

DIM fName AS STRING, Mask AS STRING

DIM X AS INTEGER

Mask = "*.*"

CurrentDir = CURDIR$

Path = AskUserForPath$()

fName = DIR$(RTRIM$(Path) + Mask)

IF LEN(fName) = 0 THEN EXIT SUB

X = 1

WHILE LEN(fName)

Files(X) = fName

fName = DIR$

INCR X

WEND

END SUB

See Also

Error Overview

How error traps work

Setting an error trap

Writing an error handler

Exiting an error handler

Error Trapping Summary


How error traps work

In PowerBASIC, error codes - returned by the ERR or ERRCLEAR functions - and error traps are local to each Sub or Function. An error trap will only trap errors that occur within the Sub or Function where it is defined.

PowerBASIC uses the following steps to determine what to do when a run-time error occurs:

  • Does an error trap exist? If so, PowerBASIC uses it.

  • If no error trap exists, PowerBASIC places an error code in the ERR and ERRCLEAR system variables and continues execution.

Consider the following:

SUB Proc1

ON ERROR GOTO ErrorTrap

' some code goes in here

CALL Proc2

' some more code goes in here

Proc1Resume:

EXIT SUB

ErrorTrap:

' Error-handling code goes in here

RESUME Proc1Resume

END SUB

See Also

Error Overview

Error Trapping

Setting an error trap

Writing an error handler

Exiting an error handler

Error Trapping Summary


Setting an error trap

To enable an error trap, use the ON ERROR GOTO statement where you want trapping enabled within the . The error-handling code must be within that procedure. An error trap is enabled only while the procedure is executing. Use the ON ERROR GOTO 0 statement where you want trapping disabled within the procedure.

See Also

Error Overview

Error Trapping

How error traps work

Writing an error handler

Exiting an error handler

Error Trapping Summary


Writing an error handler

When an error occurs and an error trap invokes your error-handling routine, the first thing the code should do is to determine which error occurred. PowerBASIC's ERR and ERRCLEAR functions return the code of the most recent error. You can use one of PowerBASIC's control structures (like SELECT CASE) to take appropriate action based on the error code. The ERROR$ function can be used to help formulate a suitable error message to log or report to the user.

See Also

Error Overview

How error traps work

Setting an error trap

Exiting an error handler

Error Trapping Summary


Exiting an error handler

You must exit an error handler with the RESUME LABEL statement. Execution branches immediately to the specified local label, and the original error trap operation is restored ready to catch the next run-time error.

In the sample program, you want to use RESUME to a specific line. Put the line label before the line that requests user input to give the user another chance to enter a correct path.

Here is the sample program, complete with Error Trapping:

SUB GetFileNames(File() AS STRING)

DIM CurrentDir AS STRING

DIM fName AS STRING, Mask AS STRING

DIM X AS INTEGER

ON ERROR GOTO ErrorTrap

Mask = "*.*"

CurrentDir = CURDIR$

GetPath:

Path = AskUserForPath$()

fName = DIR$(RTRIM$(Path) + Mask)

IF LEN(fName) = 0 THEN EXIT SUB

X = 1

WHILE LEN(fName)

Files(X) = fName

fName = DIR$

INCR X

WEND

EXIT SUB

ErrorTrap:

SELECT CASE ERRCLEAR

CASE 53 : ErrorMsg "No files in this directory."

CASE 71 : ErrorMsg "Drive not ready."

CASE 76 : ErrorMsg "That path doesn't exist."

CASE ELSE : ErrorMsg "Unknown error!"

END SELECT

RESUME GetPath

END SUB

See Also

Error Overview

Error Trapping

How error traps work

Setting an error trap

Writing an error handler

Error Trapping Summary


Error Trapping Summary

Error Trapping is a useful and powerful feature of PowerBASIC. Many programmers avoid trapping errors because of the substantial penalties other BASIC dialects impose when Error Trapping is turned on. Fortunately, PowerBASIC's hit is much lower. Still, having Error Trapping turned on may increase the size of your executable. You may wish to investigate other ways to accomplish the same results, whilst ensuring the stability of your code.

Finally, PowerBASIC now includes the following list of predefined (built-in) equates to assist in the creation of more verbose error handling code. They include:

%ERR_NOERROR | = 0
%ERR_ILLEGALFUNCTIONCALL | = 5
%ERR_OVERFLOW | = 6 (reserved)
%ERR_OUTOFMEMORY | = 7
%ERR_SUBSCRIPTPOINTEROUTOFRANGE | = 9
%ERR_DIVISIONBYZERO | = 11 (reserved)
%ERR_DEVICETIMEOUT | = 24
%ERR_INTERNALERROR | = 51
%ERR_BADFILENAMEORNUMBER | = 52
%ERR_FILENOTFOUND | = 53
%ERR_BADFILEMODE | = 54
%ERR_FILEISOPEN | = 55
%ERR_DEVICEIOERROR | = 57
%ERR_FILEALREADYEXISTS | = 58
%ERR_DISKFULL | = 61
%ERR_INPUTPASTEND | = 62
%ERR_BADRECORDNUMBER | = 63
%ERR_BADFILENAME | = 64
%ERR_TOOMANYFILES | = 67
%ERR_DEVICEUNAVAILABLE | = 68
%ERR_COMMERROR | = 69
%ERR_PERMISSIONDENIED | = 70
%ERR_DISKNOTREADY | = 71
%ERR_DISKMEDIAERROR | = 72
%ERR_RENAMEACROSSDISKS | = 74
%ERR_PATHFILEACCESSERROR | = 75
%ERR_PATHNOTFOUND | = 76
%ERR_OBJECTERROR | = 99
%ERR_GLOBALMEMORYCORRUPT | = 241 (Previously %ERR_FARHEAPCORRUPT)
%ERR_STRINGSPACECORRUPT | = 242


Graphics

This version of PowerBASIC offers an excellent graphics package for most any programming need. It's fast. It's complete. And it handles all those messy Windows details for you... automatically!

First, it's good to know that graphics in PowerBASIC are persistent. Create it once... and forget it. You'll never worry about redrawing when your window is minimized or temporarily covered. PowerBASIC handles everything. Automatically!

So, how about a quick overview? Just what can you do? First, how about some fancy text? Any font. Any size. Any color. Bold. Italic. Underline and Strikeout. Mix any combination of fonts on a single Window. Print just about anything, just about anywhere. Then add bitmaps. Stretch them or shrink them. Copy them or change them. Circles, ovals, lines and boxes. Fat lines, skinny lines, ellipses, rounded rectangles. Filled forms or empty. Colors or not. You'll create a custom scaling system -- even with fractional floating point coordinates!

So, let's get started. You should know that almost every graphical function name starts with the word . You'll find all of them together in the help file or the book.

Step one -- you'll need a canvas. A place to create these works of art. So, create a GRAPHIC WINDOW. Or two. Or ten. They'll be visible right away and give you quick feedback.

GRAPHIC WINDOW "PowerGraphics", 600, 200, 400, 300 TO hWin???

You'll get a new window, with the title "PowerGraphics". It's positioned on the upper right side of the screen at x=600, y=200. It's 400 pixels wide, and 300 pixels high.

A second option is a memory bitmap. These aren't visible at all. You create your image "behind-the-scenes", then copy or stretch it to a visible window whenever you're ready. Use GRAPHIC BITMAP NEW for a blank bitmap, or GRAPHIC BITMAP LOAD to get one from a resource or a disk file. You can have one window or five. One bitmap or twenty. As each is created, it returns a handle that you need to save. That's how you'll identify each of your canvases.

Step two | Use GRAPHIC ATTACH to choose a "graphic target". This tells PowerBASIC which window or memory bitmap to use, for the actions which follow. Until you execute another GRAPHIC ATTACH to change it again. Move back and forth, as often as necessary. There is no limitation here.
Step three | Draw-Draw-Draw. Arcs. Circles. Lines. Boxes. Text. Display them. Copy them. Save them to disk.
Step four | Clean up when you're done. You must close every graphic window with GRAPHIC WINDOW END, and every memory bitmap with GRAPHIC BITMAP END.

It's just that simple!

Some GRAPHIC functions use the concept of an implied "graphic position" to determine the default point on the graphic target where the next operation will take place. In PowerBASIC, we use the keyword POS to refer to this position (See GRAPHIC GET POS and GRAPHIC SET POS to alter or retrieve this position). POS is also commonly known as the LPR (Last Point Referenced) or even NPR (Next Point Referenced). For most purposes, you can consider these three terms to be synonymous.

When a Graphic Window or Graphic Bitmap is created, the default POS is set to (0,0), which is the upper left corner. Unless you specify otherwise, the first graphical operation starts at that point, and the completion point is then saved as the new POS. So, if you draw a line from (0,0) to (100,100), that last point (100,100) is saved as the new POS. The next line you draw would then, by default, start at (100,100), and then automatically save its completion point as the updated POS for next time.

The "Graphic Position" (POS) is used by GRAPHIC LINE, GRAPHIC PAINT, GRAPHIC PRINT, and GRAPHIC SET PIXEL. Other graphic functions neither use nor update POS.

Other GRAPHIC functions, namely those involved with the drawing of curves (GRAPHIC ARC, GRAPHIC ELLIPSE, and GRAPHIC PIE), utilize the concept of a "bounding rectangle" to determine their size and position on the graphic target. A bounding rectangle is defined as the smallest rectangle which can be drawn around the circle or ellipse. For example, let's say you wish to draw a circle centered at position (200,200), which has a radius of 50 pixels. The upper left corner (x1,y1) of the bounding rectangle would be at (150,150), while the lower right corner of the bounding rectangle would be at (250,250).


Printing

PowerBASIC supports two general classes of printers. We categorize them as Line Printers or Host Printers. Generally speaking, we recommend using Host Printers whenever possible, as they have far greater capabilities, including an extensive graphics package.

A line printer is one which will accept standard ASCII text and associated control codes, such as CR, LF, and FF. A line printer is identified by the port to which it is attached (LPT1, etc.) because data is sent directly to the port, not through a device driver. Print to Line Printers by using the LPRINT family of functions.

A host printer is one which works through the Windows printing system and a Windows printer driver. These printers are sometimes known as "Windows-only printers" or "GDI printers". They achieve device independence because the printer driver handles the task of converting ASCII text into the manufacturers proprietary binary format used by the printer. Print to Host Printers by using the XPRINT family of functions.

An interesting feature of this version is the new PRINTER$ function. This will let you retrieve both the printer name and the port name for every printer connected to the computer. Also, the new XPRINT ATTACH statement will optionally display a Printer Common Dialog to assist the user in selecting a printer, and the associated options.

In contrast to single-tasking systems like DOS, you'll need to select a printer when you're ready to print. Use either LPRINT ATTACH or XPRINT ATTACH to do that. That assures two applications won't try to print to the same printer at the same time. Then print your report. Print your graphics. Print your charts. When you're done, don't forget to detach the printer with LPRINT CLOSE or XPRINT CLOSE. This frees up the printer for another application to use. Perhaps even more important, Host Printers normally won't even begin to print to the physical paper until the print job is closed!

Some XPRINT functions use the concept of an implied "XPrint Position", to determine the default point on the host printer page where the next operation will take place. In PowerBASIC, we use the keyword POS to refer to this position (See XPRINT GET POS and XPRINT SET POS to alter or retrieve this position). POS is also commonly known as the LPR (Last Point Referenced) or even NPR (Next Point Referenced). For most purposes, you can consider these three terms to be synonymous.

When a new host printer page is created (with XPRINT ATTACH of a host printer, or XPRINT FORMFEED which ends a printer page), the default POS is set to (0,0), which is the upper left corner. Unless you specify otherwise, the first XPRINT operation starts at that point, and the completion point is then saved as the new POS. So, if you draw a line from (0,0) to (100,100), that last point (100,100) is saved as the new POS. The next line you draw would then, by default, start at (100,100), and then automatically save its completion point as the updated POS for next time.

The "XPrint Position" (POS) is used by XPRINT, XPRINT LINE, and XPRINT SET PIXEL. Other XPRINT functions neither use nor update POS.

Other XPRINT functions, namely those involved with the drawing of curves (XPRINT ARC, XPRINT ELLIPSE, and XPRINT PIE), utilize the concept of a "bounding rectangle" to determine their size and position on the host printer page. A bounding rectangle is defined as the smallest rectangle which can be drawn around the circle or ellipse. For example, let's say you wish to draw a circle centered at position (200,200), which has a radius of 50 pixels. The upper left corner (x1,y1) of the bounding rectangle would be at (150,150), while the lower right corner of the bounding rectangle would be at (250,250).


What is a Resource File?

A resource file may contain a collection of icons, menus, dialog boxes, strings tables, user-defined binary data and other types of items.

Once compiled into a suitable format, a resource file can be embedded directly into an executable or DLL file, producing a single EXE or DLL containing both code and resources. At run-time, the application can use the resource items in the embedded file. The process of creating a resource is straightforward, and is similar to compiling a PowerBASIC program.

The following sections describe the (manual) techniques involved in compiling resource scripts into a usable format, and describe the Resource Script.

See Also

Resource Editors

Resource Compiling

Resource Scripts

Converting a .DLG to a .RC

Converting a .RC to a .RES

Converting a .RES to a .PBR


Resource Editors

The most popular technique is to use a Resource Editor. A Resource Editor is a tool that lets you design and test dialog boxes visually, instead of defining individual dialog statements in a resource script by hand. Using a Resource Editor, you can add, modify, rearrange, and delete controls and resources in a resource script file.

Resource Editors such as Microsoft's "Visual Studio" and Borland's "Resource Workshop" also make it easy to place string tables, version info tables, bitmaps, icons, and other types of resources into a resource script.

See Also

What is a Resource File?

Resource Compiling

Resource Scripts


Resource File Compiling

We begin with a plain text file and compile it into a binary format that can be utilized by PowerBASIC. The plain text file is termed a Resource Script and these are stored with a .RC file extension. A Resource Compiler is then used to create a binary (.RES) file, and finally, the binary file is converted into PowerBASIC resource file format (a .PBR file).

FILE.RC >>> FILE.RES >>> FILE.PBR

The PowerBASIC IDE can be loaded with a Resource Script (.RC file) and compile the script into PowerBASIC resource file format. This is performed using the regular Compile Current File button or the RUN|Compile File menu item.

Once a .PBR file has been created, it can be embedded into an application EXE or DLL simply by using a #RESOURCE statement. During compilation, PowerBASIC automatically embeds the resource file to create a single file that contains compiled code and resources.

#RESOURCE "DIALOGS.PBR"

See Also

What is a Resource File?

Resource Editors

Resource Scripts


Resource Scripts

A resource script (.RC) file contains statements that define all of the items that will be included in the compiled binary resource file. Each statement describes a resource item, along with an identifier (ID) and any additional parameters (which vary according to the type of resource). A resource script can even reference a resource item that is stored in a separate file, such as a bitmap or icon.

A resource identifier can be numeric in the range 1 to 65535, or alphanumeric. When a PowerBASIC application needs to use a resource from the embedded resource file, it uses the resource's ID to identify it.

Hand-written scripts

There are several ways to create a resource script (.RC) file. The first technique is to write the file by hand, using a text editor like Notepad. This method is quite suitable for creating small resource scripts containing only a handful of statements.

Here is an example of a small handwritten resource script containing an icon and a version information block:

#include "resource.h"

ICON1 ICON "MYICON.ICO"

VS_VERSION_INFO VERSIONINFO

FILEVERSION 1, 5, 0, 0

PRODUCTVERSION 1, 5, 0, 0

FILEOS VOS_WINDOWS32

FILETYPE VFT_APP

BEGIN

BLOCK "StringFileInfo"

BEGIN

BLOCK "040904E4"

BEGIN

VALUE "CompanyName", "PowerBASIC, Inc.\000"

VALUE "FileDescription", "Program description\000"

VALUE "FileVersion", "01.50.0000\000"

VALUE "InternalName", "MYPROG\000"

VALUE "OriginalFilename", "MYPROG.EXE\000"

VALUE "LegalCopyright", "Copyright (c) 2006 PowerBASIC, Inc.\000"

VALUE "LegalTrademarks", "PowerBASIC is a trademark of PowerBASIC,_

Inc.\000"

VALUE "ProductName", "MYPROG\000"

VALUE "ProductVersion", "01.50.0000\000"

VALUE "Comments", "Example for Windows 95/98/NT.\000"

END

END

END

This script defines two resource items, whose alphanumeric IDs are ICON1 and VS_VERSION_INFO respectively. In this case, the actual icon binary data is stored in a separate file (MYICON.ICO). During compilation, the resource compiler takes the necessary information from the ICO file and includes it in the binary resource file it creates.

Complete details of the resource script language syntax can be found in the RC.HLP file.

See Also

What is a Resource File?

Resource Editors

Resource Compiling


Converting a .DLG to a .RC

Using a simple text editor like NOTEPAD, open the .DLG file that you created above. The file will look something like this:

DLGINCLUDE RCDATA FIXED IMPURE

BEGIN

"FILE.H\0"

END

OPTIONSLIST DIALOG 6, 18, 160, 100

STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU

CAPTION "Test Dialog"

FONT 8, "MS Sans Serif"

BEGIN

CONTROL "Group", 101, "Button", BS_GROUPBOX, 7, 8, 144, 66

CONTROL "&OK", IDOK, "Button", WS_TABSTOP, 62, 79, 40, 14

CONTROL "&Cancel", IDCANCEL, "Button", WS_TABSTOP, 111, 79, 40, 14

CONTROL "Radio", 104, "Button", BS_AUTORADIOBUTTON, 20, 23, 39,10

CONTROL "Radio", 105, "Button", BS_AUTORADIOBUTTON, 20, 38, 39,10

CONTROL "Radio", 106, "Button", BS_AUTORADIOBUTTON, 20, 53, 39,10

CONTROL "Radio", 107, "Button", BS_AUTORADIOBUTTON, 85, 23, 39,10

CONTROL "Radio", 108, "Button", BS_AUTORADIOBUTTON, 85, 38, 39,10

CONTROL "Radio", 109, "Button", BS_AUTORADIOBUTTON, 85, 53, 39,10

END

At the very top of the file, add the following line (adjusting the path to match your settings):

#include "C:\\POWERBAS\\WINAPI\\RESOURCE.H"

Note that non-capitalization of the keyword "#include" is important - it must be lowercase. Additionally, backslashes in the path must be doubled to comply with C coding conventions. Next, if the .DLG file contains the following lines near the start of the file:

DLGINCLUDE RCDATA FIXED IMPURE

BEGIN

"FILE.H\0"

END

then replace this block with the following line (also with the "#include" keyword in lowercase):

#include "FILE.H"

Finally, use the Editor's SAVE-AS function to save the file with the file extension .RC. For example, "MYAPP.RC".

Now we are ready to compile the new .RC file into .RES format. However, you may first need to copy the RESOURCE.H file into the same folder as your .RC file unless you specified the path in the #include statement.

See Also

What is a Resource File?

Resource Editors

Resource Compiling

Resource Scripts

Converting a .RC to a .RES

Converting a .RES to a .PBR


Converting a .RC to a .RES

Using the IDE

Firstly, ensure that the PowerBASIC IDE's OPTIONS dialog is configured to correctly point to the RC.EXE and PBRES.EXE files. Once configured, the IDE can automatically compile a .RC into a .RES file, and then produce a .PBR file from the .RES file. This is achieved in one simple step: simply load the .RC file into the IDE and select Compile.

Using the command-line Resource Compiler

To compile the .RC file, we need to run the Resource Compiler from a DOS box (command-line) to create the binary (.RES) resource file.

The resource compiler takes the filename of your modified (.RC) resource script file as a parameter, and produces a new 32-bit .RES file. For example:

C:\PB\BIN\RC.EXE MYAPP.RC

Note that you may need to change the path name to suit your individual settings. At this point, you should have a compiled binary resource file (i.e., MYAPP.RES), ready to be converted to PowerBASIC's .PBR format.

Tip: If you use the Microsoft Dialog Editor and you do not rename your .RC file, the resultant .RES file will overwrite the .RES file created with the Microsoft Dialog Editor. This will make it impossible to reopen the original dialog file in DLGEDIT. Be sure to rename either the .RC file or the original .RES file before compilation with RC.EXE!

See Also

What is a Resource File?

Resource Editors

Resource Compiling

Resource Scripts

Converting a .DLG to a .RC

Converting a .RES to a .PBR


Converting a .RES to a .PBR

The PowerBASIC Resource Converter (PBRES.EXE) converts binary resource files (.RES) into a compatible PowerBASIC format (.PBR). To use PBRES, open a DOS box and use the following syntax:

PBRES filename[.res]

where filename specifies a binary resource file. If a file extension is not specified, .RES is assumed.

Once PBRES converts a .RES file into .PBR format, the .PBR file can be linked into your EXE or DLL file through the #RESOURCE metastatement.

Complete details of the resource script language syntax and features can be found in the RC.HLP file.

See Also

What is a Resource File?

Resource Editors

Resource Compiling

Resource Scripts

Converting a .RC to a .RES

Converting a .RC to a .RES


Serial Communications

This section introduces telecommunications. For many programmers, writing a communications program is difficult. It is not that the programs themselves are especially long; it is that the procedures and terminology are unfamiliar. That means programs take longer to write and debug, making writing such programs frustrating. To compound the problem, performing serial communications using the Windows API can be a daunting task.

In this section, we define common communications terms and discuss some of the more regular ways of transmitting and receiving data, using the native COMM statements and related features of PowerBASIC. There are plenty of examples and some working PowerBASIC program code (included on your distribution disks) for you to try out. Feel free to use this code as a starting point for your own communications programs.

Before presenting any sample code or delving further into the mysteries of communications, let's define a few terms:

ACK | An acknowledgment signal sent by the receiver of a message.
Asynch | Asynchronous; not synchronous. The receiver and transmitter are free to send signals without matching clocks.
Baud Rate | Baud (from J. M. E. Baudot, a communications pioneer) refers to the total number of signal changes that could possibly be sent between transmitter and receiver per second. Signal changes do not necessarily mean bits, and not all bits are necessarily data, so baud rate isn't equivalent to a fixed number of characters (or even a fixed number of bits) per second.
Buffer | An area of memory used to hold transmitted or received signals before processing them.
CD | Carrier Detect. A signal used to tell that a carrier has been detected; the DCE (modem) has connected with another computer and is ready for use.
CRC | Cyclic Redundancy Check. A way of summing the data bits sent between transmitter and receiver so as to detect transmission errors.
CTS | Clear To Send. A handshaking signal that indicates the receiver is ready to receive data. Typically, a modem uses this signal for to control data flow from the computer. CTS (and sometimes DSR) are often used in response to an RTS signal.
DCE | Data Circuit-terminating Equipment. Typically, a DCE is a modem. A DCE is often inaccurately referred to as the "Data Communications Equipment".
DSR | Data Set Ready. A handshaking signal that serves to indicate that an RS-232C non-terminal device (usually a modem) is ready to receive data. Often used with CTS.
DTE | Data Terminal Equipment. Typically the computer.
DTR | Data Terminal Ready. A handshaking signal that indicates that an RS-232C serial terminal device (the computer) is ready to receive data.
Handshaking | A process whereby the receiver and transmitter match signals and correctly determine each other's status. See CTS, DSR, and RTS.
Modem | Mo dulator/Demodulator. A device used to convert digital signals to sounds that can be carried over standard telephone lines, and to convert such sounds back to digital signals.
NAK | Negative Acknowledgment. A signal sent from receiver to transmitter, indicating that an error was detected in the last message.
Null Modem | A way of connecting receive and transmit lines, so that one computer can send or receive signals directly from another without having to go through a modem. This typically requires a "null-modem" or "cross-over" cable to ensure the signals are correctly interconnected between devices.
Parity | One bit (the high-order bit) per byte sent or received, used to detect some of the possible transmission errors. Parity may be even, odd, none, mark (always on), or space (always off).
Port | A term used to refer to any one of the possibly several communications devices available to the operating system. Usually COM1, COM2, etc.
Protocol | A way of controlling transmissions or receptions. A protocol consists of a set of rules describing the form of a valid transmission, the proper response when a transmission is received, and ways of detecting and correcting errors.
RS-232 | A standard for wiring on serial communications ports, that describes which wires should carry which signals at what voltage. There are two basic standards: one for transmitting equipment (DTE) and one for receiving equipment (DCE).
RTS | Request To Send. A signal raised by the transmitter, to which the receiver should reply with CTS and/or DSR.
Serial | Refers to signals sent one bit after the other, as opposed to parallel. Parallel signals are sent more than one bit at a time.
Stop Bits | The number of bits added to each byte of data transmitted, to allow the receiver to get in step with the transmitter.
Synch | Synchronous. The receiver and transmitter match their clocks so that each will send and receive only at specific times.

See Also

Communications Basics

Communication Buffers

Parity and general error checking

Start and Stop bits

Opening a communications port

Reading and writing data

A simple communications program


Communications Basics

To communicate from one computer to another, you need a communications program on each machine, and some way to connect them (telephone lines, for example). Sometimes, those programs are built into the operating system. Even when that's the case, there will be times when you want to do something faster, more reliably, or in a different manner than what has been provided.

To communicate, you will need a way for each program to inform the other that:

1. It is ready/not ready to transmit/receive data.

2. Data has been received and is correct.

3. Data has been received and is not correct.

4. Transmission is over.

If it is not important to check for, or correct errors, items 2 and 3 in the previous list can be ignored. Those capabilities are often skipped when the programs are to be used for simple communications - short text or brief typed messages. Even the other two parts can be dropped if the programs are closely monitored, or if errors will not matter very much.

However, it is important to realize that receiving and transmitting data is not always quite as simple as it might appear, especially when you are coding under Windows. For example, suppose your program is receiving data and saving material to a disk file. What should happen if more data is received while the program is writing data to disk? Alternatively, suppose the user presses a key that means "clear the screen and display a menu" while data is being received?

Situations like this are common. The standard way of handling them is to use a buffer.

See Also

Serial Communications


Communication Buffers

Buffers can be useful in solving various types of communication or data transfer problems. For example, a printer typically includes a buffer of at least a few thousand characters. Since the computer can send material to the printer faster than it is capable of putting the material on paper, the buffer serves three purposes:

1. To even the workload for the printer

2. To allow the computer to finish sending material sooner

3. To handle occasions when the printer cannot accept more material

If the printer did not have a buffer, the computer would be forced to send data one character at a time. Until each character is received and printed, the computer should not send more. To prevent this, the printer sends a busy signal back to the computer. When it gets this signal, the computer stops until the printer sends a "ready" signal. This "ready/busy" signaling is called handshaking.

Visualize what is taking place. The printer sends a ready signal; the computer sends a character; the printer sends a busy signal, forcing the computer to wait while it prints the character; and then the whole process repeats. That is a lot of signaling for just one transmitted character!

Further, there is a possibility of error. If the computer is fast or the printer is slow (or both), it's possible for the computer to send the next character before the printer is able to signal that it's busy - something called buffer overflow. This can also happen if there is something wrong with the handshaking signals. When this happens, the printer fails to print one or more characters. Those characters have been sent by the computer, but cannot be received by the printer because there is no place to put them.

With a buffer, the printer sends a busy signal only when the buffer is full (or nearly so). That way, even if additional characters have already been sent, there will be room to store them before they are printed. Most of the time, the computer sends and the printer receives. As a result, far less signaling is necessary, and more actual data is transmitted. Therefore, a buffer makes communications between computers and printers more efficient. Since there is room to store characters transmitted, there is less chance that a character will be missed; so a buffer makes transmissions more error free, too.

In general, all communications are affected by buffering in the same way. For that reason, PowerBASIC allows you to set aside one communications buffer for received data and a separate buffer for transmitted data. In your programs, you have two responsibilities: to make sure that the buffer you use is large enough, and to empty the buffer as often as needed to prevent a buffer overflow.

How large a buffer will you need? It depends on the sort of program you are writing, and is often a matter of trial and error. At low baud rates (up to 300 baud), 256 bytes is probably adequate. Under some circumstances, 256 bytes may well be adequate at 1200 baud or higher; it all depends on how often your program checks the buffer and empties it. It's probably a good idea to use a buffer of 1024 bytes or more for 1200 baud, and it's not at all uncommon to use buffers of 4 Kilobytes or more. With the large amount of data memory available to your applications with PowerBASIC (up to 2 Gb), you could specify a receive buffer of 1 MB (or even more) and have little impact on system memory.

See Also

Serial Communications


Parity and general error checking

The ASCII character set contains 128 defined characters. It takes 7 bits to represent all 128 characters. Since there are 8 bits per byte, the eighth bit can be used to detect errors. One sort of checking adds up all the on (1) bits in each character transmitted. If the number of bits is even, the eighth bit is turned on when the character is transmitted; that forces the total number of on bits to be odd and is called odd parity. When the receiver gets the character, it performs the same procedure in reverse. If it gets the same answer as was encoded in the eighth bit, the character is accepted. If it does not, the character is in error. A related method (even parity), sums the bits and turns the parity bit on if the number of bits is odd, forcing the total number of on bits to be even.

Either method of checking the correctness of received characters is called a parity check. Unfortunately, the method can easily be thwarted if the errors are bad enough. If the transmission is relatively clean and there are few errors, a simple parity check of this sort can be reasonably effective. If any even number of data bits are reversed (on to off or vice versa), or if any odd number of bits are wrong and the parity bit is also incorrect, the parity check will fail to detect an error.

Most communications programs do not rely on parity checks, however. That is especially true if you must send a full 8 bits of data, as is the case when sending executable programs, spread sheets, some kinds of word processing files, and any kind of binary data. You should set parity to none or off whenever you need to send a full 8 bits of data.

See Also

Serial Communications

Start and Stop bits

Opening a communications port

Reading and writing data


Start and Stop bits

Stop bits are a way for a computer to "catch its breath" while sending or receiving data, while still letting the other end know that the connection is still there and is still valid; they're also used in error detection.

Stop bits are rather like parity bits. They are sent with the data, but they are not part of the data. Unlike parity bits, they are not turned on and off by the number of bits in the data; instead, they are always on. If one or more of the stop bits are missing, it constitutes a framing error.

Some computers also use start bits for a similar purpose; however, that is not as common a practice as it used to be.

The number of stop bits generally increases with higher baud rates. At 300 baud, usually 0 or 1 stop bits are used. At 1200 baud, 1 or 2 stop bits are most common.

If you connect with another computer and everything seems to be correct, but you cannot read the material you're receiving, one of three possible problems is likely. Either the baud rates are set wrong, the parity is wrong, or the number of stop (or start) bits is incorrect. If the baud rate is correct and the errors are framing errors, it is probably the number of stop bits.

See Also

Serial Communications

Communications Basics

Parity and general error checking

Opening a communications port

Reading and writing data


Opening a communications port

Before we can actually open a communications port, we must first obtain a PowerBASIC file number so we may manage input and output to the communications port. The best method of obtaining a file number is to use the FREEFILE function:

DIM hComm AS LONG

hComm = FREEFILE

The general way of opening a communications port in a PowerBASIC program is with a COMM OPEN statement. The syntax is similar to a simple random-access file OPEN, where n is the communications port number

COMM OPEN "COMn" AS #hComm

Note the trailing colon typical in DOS communications is not permitted with COMM OPEN.

If you are familiar with serial communications with DOS compilers (where all of the communications parameters are configured within a single OPEN statement), you will realize that we must instead configure these parameters individually. For this purpose, PowerBASIC offers the COMM SET statement:

COMM SET #hComm, Comfunc = value

Although configuring a serial port for communications can mean using quite a few COMM SET statements, PowerBASIC offers a greater control of the serial port than was possible before, plus a completely new method of querying existing settings and status. Retrieving a setting is performed with the COMM function, which returns a Long-integer value:

x& = COMM(#hComm, Comfunc)

Comfunc must be one of the following keywords:

Comfunc | Return values (TRUE <> 0, FALSE = 0)
BAUD | Port Baud Rate (9600, 14400, 19200, etc). See notes below.
BREAK | TRUE/FALSE Break is asserted. Break is generally used to "get the attention" of the connected modem, terminal or system.
BYTE | Number of bits per byte (4, 5, 6, 7, or 8).
CD | TRUE/FALSE Carrier Detect state. Synonym for RLSD (READ-ONLY). When CD is TRUE, the DCE (modem) has a suitable connection on the communications channel present. When CD is FALSE, there is no suitable connection.
CTSFLOW | TRUE/FALSE Enable CTS output flow control (Input signal). When CTSFLOW is enabled, it causes the DTE (computer) to stop sending data whenever the CTS signal is set to logic low by the DCE (modem). Transmission continues when the DCE (modem) sets the CTS signal back to logic high. The CTS signal is usually used in response to an RTS signal.
DSRFLOW | TRUE/FALSE Enable DSR output flow control (Input signal). When DSRFLOW is enabled, it causes the DTE (computer) to stop sending data whenever the DSR signal is set to logic low by the DCE (modem). Transmission is enabled when the DSR signal returns to logic high. The DSR signal is often used in conjunction with CTS in response to a RTS signal.
DSRSENS | TRUE/FALSE Enable DSR sensitivity. When DSRSENS is enabled, data received by the DTE (computer) is placed into the receive buffer only if DSR is set to logic high. If DSR is set low, received data is discarded. Enabling DSRSENS allows DSR to enable or disable the DTE (the computer) to receive data from the DTE (the modem). DSRSENS is rarely used in practical communications situations.
DTRFLOW | TRUE/FALSE Enable DTR handshaking flow control (Output signal). When DTRFLOW is enabled, it signals that the DCE (modem) should prepare to connect to the communications channel. DTR is usually used for modem on-hook/off-hook control, but can also be used in conjunction with DSR for handshaking.
DTRLINE | TRUE/FALSE Enable DTR line. When enabled, DTRLINE leaves the DTR line active when the port is closed by the DTE (computer). This ensures that the DCE (modem) does not close the communications channel when the port is closed.
NULL | TRUE/FALSE Null ($NUL) bytes are discarded when read.
PARITY | TRUE/FALSE Enable parity checking. This mode must be enabled for the other Parity options to be selected.
PARITYCHAR | Character to use for parity error replacement. PARITY must be enabled.
PARITYREPL | TRUE/FALSE Enable character replacement on parity error. PARITY must be enabled.
PARITYTYPE | 0 = None, 1 = Odd, 2 = Even, 3 = Mark, 4 = Space. PARITY must be enabled. Default = 0.
RING | TRUE/FALSE Ring indicator is on (READ-ONLY). When RING returns TRUE, a ringing signal is being received on the communications channel (by the modem). RING approximates the state of the ringing signal; however, it may not report accurately in all Windows platforms.
RLSD | Receive-line-signal-detect (READ-ONLY). See CD/Carrier Detect above.
RTSFLOW | Ready To Send (Output signal). 0 = Disable, 1 = Enable, 2 = Handshake, 3 = Toggle. Toggle is used for half-duplex (2-wire) operations to "reverse" the line. While the DTE (computer) is busy sending data, it raises the RTS signal, and the DCE (modem) blocks its data receive channel. When RTS signal reverts to logic low, the DCE (modem) reverts to transmit mode and the DTE (computer) switches to receive mode.
Handshake | mode causes the DTE (computer) to check the receive buffer (RXQUE) after each character is placed into the buffer. When the buffer is 5/6th full, the RTS signal is dropped. When the receive buffer drops to below 1/6th full, RTS is raised again.
RXBUFFER | Size of the receive buffer in bytes.
RXQUE | Bytes currently in the receive buffer (READ-ONLY).
STOP | 0 = 1 stop bits, 1 = 1.5 stop bits, 2 = 2 stop bits.
TXBUFFER | Size of the transmit buffer in bytes. In some cases, Windows may not be able to report the transmit size.
TXQUE | Bytes currently in the transmit buffer (READ-ONLY).
XINPFLOW | TRUE/FALSE Enable XON/XOFF input flow control. When the DTE (computer) receive buffer is full, an XOFF character is sent to the DCE (modem) to instruct it to halt transmission. When the DCE is ready to resume transmission, an XON character is sent to the DCE. Typically, XOFF is sent when the receive buffer has less than 1/16th remaining, and XON is sent when the receive buffer drops to less than 1/16th of its maximum size. Default = FALSE.
XOUTFLOW | TRUE/FALSE Enable XON/XOFF out flow control. When enabled, the DCE (modem) sends an XOFF to the DTE (computer) to halt data transmission to the DCE. When the DCE is ready to receive more data, an XON character is sent. XOUTFLOW typically uses the same 1/16th rules as XINPFLOW. Default = FALSE.

Common baud rates range from 110 to 256000. There are equates defined in the WIN32API.INC file, prefixed with %CBR_ to assist you with specifying a common baud rate, but you are not restricted to a limited set of rates.

To open a communication port and initialize it for use, you will need to set the following parameters. The values are for demonstration purposes, you may choose your own settings as necessary.

' Minimum recommended settings

COMM SET #hComm, BAUD = 9600 ' 9600 baud

COMM SET #hComm, BYTE = 8 ' 8 bits

COMM SET #hComm, PARITY = %FALSE ' No parity

COMM SET #hComm, STOP = 0 ' 1 stop bit

COMM SET #hComm, TXBUFFER = 2048 ' 2 Kb transmit buffer

COMM SET #hComm, RXBUFFER = 4096 ' 4 Kb receive buffer

' Optional settings for flow control

COMM SET #hComm, CTSFLOW = 1 ' Enable CTS flow control

COMM SET #hComm, RTSFLOW = 1 ' Enable RTS flow control

COMM SET #hComm, XINPFLOW = 0 ' Disable XON/OFF Input

' flow control

COMM SET #hComm, XOUTFLOW = 0 ' Disable XON/XOFF output

' flow

When we have finished using our communication channel, we can terminate it using the COMM CLOSE function:

COMM CLOSE #hComm

If any errors occur when attempting to open the communications port, or as a result of an invalid Comfunc value, PowerBASIC will set the ERR system variable.

See Also

Serial Communications

Reading and writing data

A simple communications program


Reading and writing data

To complement the new COMM OPEN statement, PowerBASIC introduces four new statements to help you write serial communications programs:

COMM PRINT #hComm, expr [;]

COMM SEND #hComm, expr

COMM RECV #hComm, count, expr

COMM LINE [INPUT] #hComm, expr

COMM PRINT and COMM SEND are used to send data out of the communications port (via the transmit buffer). COMM PRINT sends the data specified by expr followed by a CR/LF byte pair {$CRLF or CHR$(13,10)}. By adding a trailing semicolon to the COMM PRINT statement, PowerBASIC suppresses these CR/LF bytes. COMM SEND is identical to COMM PRINT with a trailing semicolon.

COMM RECV and COMM LINE [INPUT] are used to receive data from a communications port (via the receive buffer). The COMM(#hComm, RXQUE) function can be used to identify the number of bytes that can be retrieved with COMM RECV. COMM LINE is used to return a CR/LF delimited "line" of data from the receive buffer.

If your communications application is primarily dealing with binary data transmission and reception, COMM SEND and COMM RECV will suit this purpose exactly. COMM PRINT and COMM INPUT are very useful for sending "AT" commands to a modem and receiving the modem response. For example:

COMM PRINT #hComm, "AT"

SLEEP 1000 ' Give modem time to respond

WHILE COMM(#hComm, RXQUE)

COMM LINE #hComm, a$

' Display "AT" (the modem echo),

' followed by "OK" (the modem response)

#IF %DEF(%PB_CC32)

PRINT a$

#ELSE

MSGBOX a$

#ENDIF

WEND

The COMM RESET statement allows you to switch off all flow control during a serial communications session.

COMM RESET #hComm, FLOW

See Also

Serial Communications

Communications Basics

Communication Buffers

A simple communications program


A simple communications program

Let us assume you want a simple communications program to use for accessing a local computer bulletin board (BBS). You know the parameters for the board: it is 14400 baud, 8 data bits, one stop bit, and no parity.

You want to display data on your screen, to type data, and have it sent to the bulletin board. You intend to use a modem connected to COM1. The following short program serves as a starting point:

#COMPILE EXE

#DIM ALL

%TRUE = -1

%FALSE = 0

DECLARE SUB Display(BYVAL sData AS STRING)

FUNCTION PBMAIN

LOCAL hComm AS LONG

LOCAL Echo AS LONG

LOCAL Qty AS LONG

LOCAL Stuf AS STRING

LOCAL MyInput AS STRING

hComm = FREEFILE

COMM OPEN "COM1" AS #hComm

IF ERRCLEAR THEN EXIT FUNCTION 'Exit if port cannot be opened

COMM SET #hComm, BAUD = 14400 ' 14K4 baud

COMM SET #hComm, BYTE = 8 ' 8 bits

COMM SET #hComm, PARITY = %FALSE ' No parity

COMM SET #hComm, STOP = 0 ' 1 stop bit

COMM SET #hComm, TXBUFFER = 1024 ' 1 Kb transmit buffer

COMM SET #hComm, RXBUFFER = 1024 ' 1 Kb receive buffer

Echo = -1 ' Set echo ON

WHILE %TRUE ' loop forever

WHILE NOT INSTAT ' unless key pressed

Qty = COMM(#hComm, RXQUE)

IF ISTRUE Qty THEN

COMM RECV #hComm, Qty, Stuf ' read incoming characters

Display Stuf ' display the raw data

END IF ' transmitter

WEND

WHILE INSTAT ' Any keypresses?

MyInput = INKEY$ ' get them

IF MyInput = $ESC THEN Terminate

COMM SEND #hComm, MyInput ' send typed characters

IF Echo THEN Display MyInput ' display them

WEND

WEND ' check for more incoming characters

Terminate:

COMM CLOSE #hComm ' Close the comm port and exit

END FUNCTION

SUB Display(BYVAL sData AS STRING) ' handles embedded CR/LF bytes

LOCAL sDataPtr AS BYTE PTR

LOCAL y AS LONG

REPLACE $LF WITH "" IN sData ' reduce CR/LF to CR

sDataPtr = STRPTR(sData)

FOR y = 0 TO LEN(sData) - 1

IF @sDataPtr[y] = 13& THEN

PRINT ' force new line on CR

ITERATE FOR

END IF

PRINT CHR$(@sDataPtr[y]); ' display current char

NEXT y

END SUB

The above program allows you to connect with the bulletin board, but it will not dial the number of the bulletin board through the program itself. You can do that easily, though, in one of two ways, as follows:

You can dial the bulletin board manually. When you are done dialing, connect the telephone line to the modem (or press a button on your modem, switching the line from the telephone back to the modem). The program should now be ready to receive whatever the bulletin board sends.

You can send the appropriate signals directly to the modem itself. Most modems recognize a common command set originated by the Hayes Company. To initialize the modem and dial, you would enter the following commands:

ATZ

ATDT18005551212

Note: some modems require capital letters for AT commands. Lowercase letters will not work.

After you have entered the ATZ command, the modem responds. You will see the message "OK" on your screen. After you have entered ATDT and the telephone number, the modem's lights flicker for a moment. If your modem is capable of making a sound, you will hear the sounds of the number being dialed and the telephone ringing at the other end.

If the number is busy, you may hear a busy signal through your modem speaker, or you may not hear anything more. If the connection is made, you may see some garbage characters on your screen.

At this point, many users become concerned and think that something must be wrong. Why are there illegible characters onscreen? Relax: this happens often. The computer you called does not yet know what baud rate and communications parameters you are using. In most cases, you should press ENTER a few times; the computer at the other end will use that character to determine what your parameters are and will adjust itself accordingly. Soon afterward, you should see a welcoming message. You may now type whatever you like.

If you see doubled characters, change the line that sets the value of echo. Instead of setting echo to -1, set it to 0. If you wish to send a stream of AT commands to a modem in quick succession, you may be required to add a small delay between each AT command in order to give the modem time to decode each command and respond appropriately. A delay of 100 to 200 milliseconds (mSec) is usually sufficient.

Using disk files

The sample program above does not let you save material to a disk file, or send data from a disk file to the bulletin board. Nevertheless, those two options are very useful. How do you do it?

Let us suppose you wanted to send a disk file to the bulletin board. To do that, the routine that sends your keystrokes to the bulletin board must be altered. The usual way to do this is to assign a special keystroke a different meaning. Instead of being sent, the keystroke is interpreted as a command to get the name of a disk file, read that disk file, and then send it to the bulletin board.

Let us make CTRL+T mean "transmit a disk file". CTRL+T is ASCII code 20; alter the previous program to check for that character. To do that, add one line immediately after the line reading:

MyInput = INKEY$

The added line reads:

IF MyInput = CHR$(20) THEN GOSUB ReadDiskFile

Now add the following DIM statements immediately after the original set of DIM statements (just under the top of the PBMAIN function header):

' ReadDiskFile variables

LOCAL FileNam AS STRING

LOCAL hFile AS LONG

LOCAL FileLen AS LONG

LOCAL Chunk AS LONG

LOCAL i AS LONG

LOCAL Stuf2 AS STRING

Now add the following code immediately after the EXIT FUNCTION and before the END FUNCTION statement at the bottom of the file:

ReadDiskFile:

' a subroutine to read a disk file and transmit it

LINE INPUT "Name of the disk file: "; FileNam

hFile = FREEFILE

OPEN FileNam FOR BINARY AS #hFile 'open as binary file

FileLen = LOF(hFile) 'get the file length

Chunk = COMM(#hComm, TXBUFFER) \ 2 'use 1/2 TX buffer per

'chunk

FOR ix = 1 TO FileLen \ Chunk

GET$ #hFile, Chunk, Stuf2 'read the file

COMM SEND #hComm, Stuf2 'and send it

NEXT

IF FileLen MOD Chunk <> 0 THEN 'is there more to send?

GET$ #hFile, FileLen MOD Chunk, Stuf2

COMM SEND #hComm, Stuf2

END IF

CLOSE #hFile

MyInput = "" 'don't send the original CTRL+T

RETURN

The routine works, but there's no error-checking in it. If the disk file does not exist, nothing is sent, but a zero-length file is created. If you enter an illegal file name, the program will set the ERR system variable to indicate that [a potentially fatal] error has occurred. You will probably want to add some kind of error checking to the program for those reasons. See Errors and Error Trapping for detailed information on error trapping.

To receive a disk file, we will use CTRL+R (ASCII 18); you can choose another key if you like. However, things are not quite that simple: there should also be a way to stop receiving a disk file. We will use a second CTRL+R to stop receiving to a disk file as well. To accomplish this, you will need yet another subroutine.

After the line:

IF MyInput = CHR$(20) THEN GOSUB ReadDiskFile

Add the following line:

IF MyInput = CHR$(18) THEN GOSUB WriteDiskFile

Now add the following variables immediately after the existing LOCAL statements:

' WriteDiskFile variables

LOCAL Already AS LONG

LOCAL hFile2 AS LONG

Here is the new subroutine. Place this after the ReadDiskFile routine we added:

WriteDiskFile:

' subroutine to write received data to a disk file

MyInput = ""

IF Already THEN ' already writing to disk file so stop

RESET Already

CLOSE hFile2 ' close the file

PRINT "Finished writing file!"

RETURN

END IF

hFile2 = FREEFILE

Already = 1 ' set flag when file is open

LINE INPUT "Output file name: "; FileNam

OPEN FileNam FOR APPEND AS #hFile2

RETURN

The output file is now open. The received data will be appended to the end of any existing file of that name. However, we have not provided any way to actually save any of that information. To do that, add one more small line of code immediately after the line:

COMM RECV #hComm, Qty, S tuf ' read incoming characters

The added line reads:

IF Already THEN PRINT #hFile2, Stuf; ' save to disk file

Finishing touches

If we examine this example file, we find that we have overlooked one problem: if the program is terminated while the output file is in use, the file is not closed.

While this is not a fatal condition, it is a poor approach to program design: we should always close the files we have opened. Remembering to perform this chore will stand you in good steed when it comes to using the Windows API functions. In many cases, failing to close a registry key or delete a GDI object can cause bugs, both deceptive and difficult to locate, or memory leaks that reduce system memory even after your program has ended. The golden rule should always be before you leave, clean up after yourself.

So, faced with this problem, how do we know if the output file is open before we end the program? PowerBASIC provides an effective mechanism to test the state of a file number: the FILEATTR function.

After the line that reads:

COMM CLOSE #hComm ' Close the comm port and exit

We add the following line to the file:

IF ISTRUE hFile2 AND ISTRUE FILEATTR(hFile2, 0) THEN CLOSE #hFile2

This line initially tests for a non-zero value in hFile2. Because PowerBASIC uses Short-circuit evaluation within IF/THEN statements, the FILEATTR function is only called if the file number variable is found to be non-zero (logical TRUE). FILEATTR with a 0 in the second parameter position returns a non-zero result if the file is still open.

In this instance, we control three possible scenarios with only one line of code:

1. The output file feature was not used (hFile2 = 0)

2. The output file remained open when the program was about to end (hFile2 <> 0)

3. The output file had been used, but had been closed before program termination (hFile2 <> 0)

It is true that we could have just closed the file associated with hFile2 regardless of the state of the file or the value of the file number. However, in most programming circles, that is considered to be a bad approach. It is always better to write code that is fail-safe in as many conditions as possible.

For final improvements, we have added another small feature that allows the user to change the Echo State while the program is running. We have written an addition to the program so that pressing CTRL+E changes Echo from 1 to 0 or from 0 to 1.

The final program can be found in the PB\SAMPLES\COMM folder of your PowerBASIC installation. It is not very large, but it handles a surprising number of ordinary communications tasks. It lacks some error checking, as has been noted. If you choose to modify this program, you might want to put some error checking in. You might also want to allow the operator to enter no file name, and return with no damage done.

In addition, the structure of the program puts first priority to keyboard input. If the operator takes too long to enter a file name, and the other computer is sending material at the same time, the input buffer will overflow. Maybe you can have the program check for that possibility and store the incoming buffered data into a temporary string, or perhaps expand the input buffer to more than 1024 bytes. At 1200 baud, with no parity, 8 data bits, and 1 stop bit, it takes about eight seconds for the buffer to fill. With a buffer of 8192 bytes, it takes over a minute for the buffer to fill; that should be plenty of time for the operator to enter a file name. With modern modem speeds of 56 Kbps, you are likely to find that a much larger buffer is required.

If you see doubled characters onscreen whenever you type, the other computer is echoing (sending back a copy of) what you type. To stop this echoing, press CTRL+E. By experimenting and adding features here and there, you can get a very effective program that does exactly what you want.

See Also

Serial Communications

Communications Basics

Communication Buffers

Parity and general error checking

Start and Stop bits

Opening a communications port

Reading and writing data


TCP and UDP Communications

Network Communications is one of the hottest programming topics today. Whether you need to send an email message to an SMTP server on your Intranet, or transfer a file from a remote Internet server halfway around the world, PowerBASIC can handle your network communications requirements.

Networks typically consist of many computers, all with a number of different hardware architectures and operating systems. Your local area network might have machines running Windows, Linux, DOS, OS/2, or Mac OS. Your network may use IPX, ATM, or some other transmission protocol for sending data packets from one computer to the next. The architects of the Internet needed a transmission protocol that could be used on any platform.

See Also

The Internet Protocol (IP)

User Datagram Protocol (UDP)

Transmission Control Protocol (TCP)

Winsock

Request for Comments (RFC)

TCP clients and servers

Simple Mail Transfer Protocol (SMTP)

An ECHO client and server using TCP


The Internet Protocol (IP)

The Internet Protocol was designed for transmitting blocks of data called datagrams from a source location, to one or more destinations. It is specifically limited to provide the functions necessary to deliver a datagram from a source to a destination over an interconnected system of networks. There is no functionality for data reliability, flow-control, or sequencing. It works by using one local network to connect with another local network, until the datagram is delivered to its destination - although, a datagram does not have to leave the local network at all if the destination does not reside outside of the network.

The source and the destination are specified as numeric addresses, also known as IP addresses. An IP address consists of four bytes. The combined sequence of the four bytes is unique for each connection to the network (a single computer can have more than one connection to the network, and therefore can have more than one IP address).

Let's say that you want to send the message "Hello" from your computer to another computer on the network. Your computer cannot simply transmit the 5 bytes of your message over the network cable. It first has to create a datagram. In simple terms, the datagram would include the identity of your computer (the sender), the identity of the computer you are sending the message to, and some kind of checksum that allowed the receiving computer to verify that the datagram arrived intact. Your computer would deliver that datagram to a host or gateway on your network.

The gateway will then determine where the datagram needs to go next. If the destination computer is on the same local network, it may simply deliver it to the destination. If the destination is outside of the local network, the datagram is delivered to another host or gateway "downstream" from your network. After that, either that host or gateway would then send the datagram even further downstream toward the destination; or if the destination resides on the local network of the current host or gateway, it will deliver the datagram to the final destination itself.

During this journey, it is possible for the datagram to become corrupted, be misrouted (and lost), or simply expire because the journey was too long. The Internet Protocol does not provide any notification capabilities to inform the sender of a delivery problem. It is also possible for large datagrams to be chopped into multiple smaller datagrams if any host or gateway along the path cannot handle the size of the datagram. Each datagram is then broken into as many smaller datagrams as it needs to hold all of the data. Those datagrams then have to be reassembled at the destination.

See Also

User Datagram Protocol (UDP)

Transmission Control Protocol (TCP)

Winsock

Request for Comments (RFC)

TCP clients and servers

Simple Mail Transfer Protocol (SMTP)

An ECHO client and server using TCP


User Datagram Protocol (UDP)

Obviously, writing code to deal with reassembling fragmented datagrams would make you think twice about how badly your application needs to communicate over a network. Fortunately, the Internet architects provided a protocol layer that sits on top of the Internet Protocol.

UDP uses the Internet Protocol to send datagrams from a source to a destination. When the datagram arrives at the destination, it hands the complete datagram packet to the client. If the datagram was fragmented along the way, it reassembles the fragments into a complete datagram beforehand.

Like the Internet Protocol it uses, UDP does not guarantee delivery of a datagram. Its purpose is simply to format a datagram with your data, send it via the Internet Protocol to a destination, and at the destination, deliver the complete datagram to a client.

One interesting aspect of the Internet Protocol is that datagrams can be delivered to a destination in a different sequence than the one in which they were sent. For example: your application sends two datagrams to another computer. The first datagram is routed along a longer path than the second datagram, and therefore arrives at the destination after the second datagram has arrived.

See Also

The Internet Protocol (IP)

Transmission Control Protocol (TCP)

Winsock

Request for Comments (RFC)

TCP clients and servers

Simple Mail Transfer Protocol (SMTP)

An ECHO client and server using TCP


Transmission Control Protocol (TCP)

TCP is a connection-based protocol layer that guarantees delivery of data to the destination. The reliability and flow control of TCP requires that status information be sent with each datagram indicating sequence numbers and checksums. So that TCP transmissions can recover from data that is damaged, lost, duplicated, or delivered out of order, each datagram is checked for its sequence number, and the data is verified against the checksum. An acknowledgment (ACK) is then required from the recipient for each successful datagram received. If an ACK is not received within a timeout period, the datagram is resent.

Unlike UDP, TCP does not reassemble fragmented datagrams into the original data packet. It simply extracts the data portion of the datagram and adds it to the incoming data stream. This can be problematic if a source has sent 20 bytes of data that is fragmented into two datagrams with 10 bytes each. TCP will give the first 10 bytes to the client without waiting for the next 10 bytes to arrive. UDP will give all 20 bytes of the data, or nothing.

See Also

The Internet Protocol (IP)

User Datagram Protocol (UDP)

Winsock

Request for Comments (RFC)

TCP clients and servers

Simple Mail Transfer Protocol (SMTP)

An ECHO client and server using TCP


Winsock

In Windows, Microsoft has encapsulated the Internet Protocol and the TCP and UDP protocol layers into the Windows Sockets Layer, or "Winsock". Winsock allows an application to send datagrams using either TCP or UDP without having to do low-level programming to create IP datagram packets, deal with receipts and acknowledgments, or reassemble fragmented datagrams.

PowerBASIC further encapsulates the process by handling DNS resolution of IP addresses, and presents statements familiar to the programming model used by BASIC programmers. You are free to concentrate on the data being sent and ignore the details of sending it.

PowerBASIC requires version 2.0 or later of Winsock.

See Also

The Internet Protocol (IP)

User Datagram Protocol (UDP)

Transmission Control Protocol (TCP)

Request for Comments (RFC)

TCP clients and servers

Simple Mail Transfer Protocol (SMTP)

An ECHO client and server using TCP


Request for Comments (RFC)

All of the technical specifications for the Internet are contained in white papers called "Request for Comments". For example, the RFC document describing the UDP protocol is RFC768.TXT and can be downloaded from http://www.rfc-editor.org.

See Also

The Internet Protocol (IP)

User Datagram Protocol (UDP)

Transmission Control Protocol (TCP)

Winsock

TCP clients and servers

Simple Mail Transfer Protocol (SMTP)

An ECHO client and server using TCP


TCP clients and servers

The Internet Protocol driver in Winsock actually sends and receives the datagrams itself, and the UDP and TCP layers take care of data integrity. However, to actually communicate with another computer over the Internet your code will have to handle the data itself. That is typically done using a high-level protocol such as SMTP, POP3, FTP, and others.

Think of IP as the telephone wire that carries a voice from a transmitter of one telephone to a receiver of another telephone. UDP and TCP are simply different types of telephones that make sure each sound is received exactly as it was sent. Therefore, SMTP, POP3, FTP, etc, should be considered the language that you use to speak. Both the caller and the person being called need to speak the same language if they wish to understand the conversation. Obviously, you cannot speak Latin to a person who only understands English or French.

See Also

The Internet Protocol (IP)

User Datagram Protocol (UDP)

Transmission Control Protocol (TCP)

Winsock

Request for Comments (RFC)

Simple Mail Transfer Protocol (SMTP)

An ECHO client and server using TCP


Simple Mail Transfer Protocol (SMTP)

One of the easiest high-level TCP protocols to use is SMTP for sending an email message. This application simply connects to an SMTP server via TCP, identifies itself, and identifies who the message is for, sends the text of the message, and finally says goodbye. As each line of text is sent to the server, it returns a status code and message to indicate progress. The following code demonstrates this:

#COMPILE EXE

' Be sure to change the following two string equates

' to the name of your SMTP mail server and your email

' address.

$mailhost = "mailserver.mydomain.com"

$mailfrom = "email@address.com"

FUNCTION PBMAIN() AS LONG

' Get the local computer's IP address and name

HOST ADDR TO ip&

HOST NAME ip& TO hostname$

' ** Connect to the mailhost

hTCP& = FREEFILE

TCP OPEN "smtp" AT $mailhost AS hTCP&

IF ERR THEN

PRINT "Error connecting to mailhost"

EXIT FUNCTION

ELSE

TCP LINE hTCP&, buffer$

IF LEFT$(buffer$, 3) <> "220" THEN

PRINT "Mailhost Error: "; buffer$

EXIT FUNCTION

END IF

END IF

' ** Greet the mailhost

TCP PRINT hTCP&, "HELO " + hostname$

TCP LINE hTCP&, buffer$

IF LEFT$(buffer$, 3) <> "250" THEN

PRINT "HELO Error: "; buffer$

TCP CLOSE hTCP&

EXIT FUNCTION

END IF

' ** Tell the mailhost who we are

TCP PRINT hTCP&, "MAIL FROM:<" & $mailfrom & ">"

TCP LINE hTCP&, buffer$

IF LEFT$(buffer$, 3) <> "250" THEN

PRINT "MAIL FROM Error: "; buffer$

TCP CLOSE hTCP&

EXIT FUNCTION

END IF

' ** Tell the mailhost who we want to send the message to

TCP PRINT hTCP&, "RCPT TO:<info@powerbasic.com>"

TCP LINE hTCP&, buffer$

IF LEFT$(buffer$, 3) <> "250" THEN

PRINT "RCPT TO Error: "; buffer$

TCP CLOSE hTCP&

EXIT FUNCTION

END IF

' ** Now we can send the message

TCP PRINT hTCP&, "DATA"

TCP LINE hTCP&, buffer$

IF LEFT$(buffer$, 3) <> "354" THEN

PRINT "DATA Error: "; buffer$

TCP CLOSE hTCP&

EXIT FUNCTION

END IF

TCP PRINT hTCP&, "From: " & $mailfrom

TCP PRINT hTCP&, "To: info@powerbasic.com"

TCP PRINT hTCP&, "Subject: Greetings!"

TCP PRINT hTCP&, "Just wanted to say hello."

TCP PRINT hTCP&, "This TCP stuff is great!

' ** End of the message

TCP PRINT hTCP&, "."

TCP LINE hTCP&, buffer$

IF LEFT$(buffer$, 3) <> "250" THEN

PRINT "DATA Error: "; buffer$

TCP CLOSE hTCP&

EXIT FUNCTION

END IF

' ** Say goodbye

TCP PRINT hTCP&, "QUIT"

TCP LINE hTCP&, buffer$

IF LEFT$(buffer$, 3) <> "221" THEN

PRINT "QUIT Error: "; buffer$

TCP CLOSE hTCP&

EXIT FUNCTION

END IF

TCP CLOSE hTCP&

END FUNCTION

The SMTP protocol is fully described in RFC 821 http://www.rfc-editor.org

See Also

TCP and UDP Communications

TCP clients and servers

An ECHO client and server using TCP


An ECHO client and server using TCP

The simplest TCP server application is an Echo Server (RFC 862). It simply listens to port 7, and when it receives a data packet, it returns the data packet back to the client.

Writing a TCP server in PowerBASIC requires some advanced programming. The Winsock API will only send notification requests to a window. Although a "Console" is technically a window, it cannot receive TCP notification because an application cannot gain access to the callback for the console window. It is therefore necessary to use the Windows API to create a Window for the application to receive notifications. The following function will register a window class, and create a hidden window that can be used by your server.

FUNCTION MakeWindow() AS DWORD

LOCAL wce AS WndClassEx

LOCAL szClassName AS ASCIIZ * 80

LOCAL hWnd AS DWORD

STATIC registered AS LONG

IF ISFALSE registered THEN

szClassName = "PBTCPCOMM"

wce.cbSize = SIZEOF(wce)

wce.style = %NULL

wce.lpfnWndProc = CODEPTR(TcpProc)

wce.cbClsExtra = 0

wce.cbWndExtra = 0

wce.hInstance = GetModuleHandle(BYVAL %NULL)

wce.hIcon = %NULL

wce.hCursor = %NULL

wce.hbrBackground = %NULL

wce.lpszMenuName = %NULL

wce.lpszClassName = VARPTR(szClassName)

wce.hIconSm = %NULL

RegisterClassEx wce

registered = %TRUE

END IF

hWnd = CreateWindow("PBTCPCOMM", "", 0,0,0,0,0, %NULL, _

%NULL, GetModuleHandle(BYVAL %NULL), BYVAL %NULL)

ShowWindow hWnd, %SW_HIDE

FUNCTION = hWnd

END FUNCTION

To create a TCP server, your program must first open a socket using the TCP OPEN SERVER statement. Then, when a client contacts your server, this socket will receive the notification. To specify which notifications your code will process, use the TCP NOTIFY statement:

%TCP_ACCEPT = %WM_USER + 4093 ' user-defined message value

...

hServer = FREEFILE

TCP OPEN SERVER PORT 7 AS #hServer

TCP NOTIFY hServer, ACCEPT TO hWnd AS %TCP_ACCEPT

TCP NOTIFY tells Winsock that it should send the %TCP_ACCEPT message to the window specified by hWnd. Your callback will then include a message handler for the %TCP_ACCEPT message. The lParam& parameter to your callback will tell you what type of notification was sent:

%TCP_ECHO = %WM_USER + 4094 ' user-defined message value

...

CASE %TCP_ACCEPT

SELECT CASE LO(WORD, lParam&)

'* An ACCEPT notification was sent

CASE %FD_ACCEPT

hEcho = FREEFILE

TCP ACCEPT hServer AS hEcho

TCP NOTIFY hEcho, RECV CLOSE TO hWnd AS %TCP_ECHO

.

. 'other notification code goes here

.

END SELECT

Once your code receives the ACCEPT notification, it uses the TCP ACCEPT statement to "close" the socket. A new socket is created for the actual communication with the client. The original socket (hServer) is used strictly to process ACCEPT notifications only. TCP NOTIFY is then used with the new socket handle to process RECV and CLOSE notifications.

When the Echo Client sends its message to your server, a RECV notification will be sent to your window. Your code can then log the incoming message, and send it right back to the client. When the CLOSE notification is received, you can close the socket:

CASE %TCP_ECHO

SELECT CASE LO(WORD, lParam&)

CASE %FD_READ

IF hEcho <> %INVALID_SOCKET THEN

TCP RECV hEcho, 1024, buffer

TCP SEND hEcho, buffer

LogEvent $DQ + Buffer + $DQ

END IF

CASE %FD_CLOSE

TCP CLOSE hEcho

hEcho = %INVALID_SOCKET

END SELECT

To connect with the Echo Server, our Client simply needs to open a socket at port 7, send a string, and display the string echoed back from the server.

FUNCTION PBMAIN() AS LONG

LOCAL hSocket AS LONG

hSocket = FREEFILE

TCP OPEN PORT 7 AT "" AS hSocket

IF ERR THEN

PRINT "OPEN Error"; ERR

EXIT FUNCTION

END IF

IF LEN(COMMAND$) = 0 THEN

TCP SEND hSocket, "This is a test"

ELSE

TCP SEND hSocket, COMMAND$

END IF

TCP RECV hSocket, 1024, buffer$

IF ERR THEN

PRINT "RECV Error"; ERR

EXIT FUNCTION

END IF

PRINT buffer$

TCP CLOSE hSocket

END FUNCTION

The complete Echo Server and Echo Client sample can be found in your PB\SAMPLES\INTERNET\TCP folder.

Finally, it should be noted that there is no direct correlation between the number of TCP SEND statements executed, compared to the number of %FD_READ messages received. This is because Winsock may concatenate multiple data packets and issue a lesser number of %FD_READ messages in response. Therefore, it is usually necessary to construct your code so that it continues to read data from the incoming data stream until either the returned string is empty, or an error is detected. For example:

DIM InBuffer AS STRING

...

CASE %FD_READ

InBuffer = ""

IF hEcho = %INVALID_SOCKET THEN EXIT SELECT

DO

TCP RECV hEcho, 1024, buffer

IF LEN(buffer) = 0 OR ISTRUE ERR THEN EXIT LOOP

InBuffer = InBuffer + buffer

TCP SEND hEcho, buffer

LogEvent $DQ + Buffer + $DQ

LOOP

...

See Also

TCP and UDP Communications

Simple Mail Transfer Protocol (SMTP)


The Inline Assembler

Occasionally, you may run into a situation where the syntax and structure of the BASIC language is not the most suitable for a task at hand. PowerBASIC addresses the need for optimal speed and flexibility with its built-in assembler. Inline assembly is the process of embedding assembly-language statements (opcodes) within the overall structure of your BASIC code. Those statements are compiled along with your BASIC code without the need for an external assembler.

This chapter discusses the different ways that PowerBASIC lets you use assembly-language code in your BASIC programs. It also discusses some design philosophies and considerations, which you should keep in mind if you decide to write your own assembly-language procedures or functions.

The technique of interfacing with assembly-language is, by its very nature, somewhat complex. You should be reasonably familiar with assembly-language concepts before tackling the information in this chapter.

See Also

Using assembly-language in your code

Flat memory model

Inline Assembler code syntax

Protected mode programming

Mnemonics and Operands

Opcodes and Mnemonics

Registers

Data types in registers

MMX registers

The stack

Balancing the stack

Tricks of the stack

Stack Overhead Reduction

Saving registers

Saving Registers at the Sub/Function level

Intermixing ASM and BASIC code

Using ESP and EBP

Saving the FPU registers

Tricks in preserving registers

Addressing and pointers

Effective addressing

Passing parameters

Parameters passed by reference or by copy

Parameters passed by value

Passing dynamic strings

Passing arrays

Accessing PowerBASIC variables by name

Commenting Assembly code


Using assembly-language in your code

PowerBASIC provides a number of ways in which you can use assembly-language. You can write the whole program using the Inline Assembler. You can write entire Subs and Functions in assembly-language, or, you can write individual lines of code in assembler, surrounded by BASIC statements. This ability to intermix BASIC and assembly-language, line by line, makes PowerBASIC's Inline Assembler a very powerful tool when optimal performance is an essential issue.

To write good assembler code, you must be aware of certain items:

  • The types of variables supported by PowerBASIC

  • How those variables are stored in memory

  • How to use variable names in your Inline Assembler routines

  • Which registers to save (and restore)

  • How to pass arguments (to and from Inline Assembler routines)

  • The need to pop everything you push

  • The differences between near and far calls

  • The rules to follow when writing assembly-language routines

See Also

The Inline Assembler

Inline Assembler code syntax


Inline Assembler code syntax

The ASM statement or, for a "shortcut", the exclamation point (!), is used to insert assembler instructions (or opcodes) into your BASIC program. They must appear at the beginning of each line that contains an assembler instruction. The Inline Assembler supports standard instructions and registers, including 8086/8088, 80286, 80386, 80486, Pentium/MMX and 32-bit floating-point opcodes as defined in the Intel Reference Manuals, and can be downloaded from http://developer.intel.com/.

The machine code generated by ASM statements is placed directly in line with the code from your BASIC statements, so execution of your program will flow just as it appears in your source code. You should never, under any circumstances, attempt to exit a Sub/Function early by the use of a RET instruction, as that guarantees failure. If you need to terminate a routine at some point before the End Sub/Function statement, jump to a label at the end of the procedure instead.

See Also

The Inline Assembler

Using assembly-language in your code

Flat memory model


Flat memory model

A program written in native 32 bit Windows format is created in what is called FLAT memory model that has a single segment, which contains both code and data. Such programs must be run on a 80386 or higher processor.

Differing from earlier 16-bit code that used combined segment and offset addressing with a 64 Kb segment limit, FLAT memory model works only in offsets and has a range of 4 Gigabytes. This makes assembly code easier to write and the compiled (assembled) code is generally a lot faster than the equivalent 16-bit code.

All segment registers are automatically set to the same value with the flat memory model. This means that segment / offset addressing must NOT be used in 32-bit programs that run in 32-bit Windows operating systems.

For programmers who have written code in DOS, a 32-bit Windows PE (executable) file is similar in some respects to a DOS COM file - they have a single segment that can contain both code and data and they both work directly in offsets. That is, neither uses segment / offset addressing.

Flat-model assembler code defaults to NEAR code addressing and NEAR data addressing within the range of 4 gigabytes.

The FS and GS segment registers are rarely (if ever) used in application programs but may be used in some instances by the operating system itself.

See Also

The Inline Assembler

Protected mode programming

Mnemonics and Operands


Protected mode programming

DLLs and EXEs generated by the PowerBASIC 32-bit compilers require Microsoft Windows 95 or later, or Windows NT 3.10 or later. This includes Windows 98, Windows ME, Windows NT 4.0, Windows 2000, Windows XP, and so on. All of these operating systems run your program in protected mode, using a 32-bit flat memory model.

In real-mode operating systems such as DOS, it was possible to overwrite sections of the operating system code if a program was not correctly written. This would crash the operating system and the computer would require a reboot before it would run again.

Protected-mode memory is designed to prevent this from happening. It uses a protected mode memory manager to control the address range that all applications can read and write to, and the memory manager terminates any application that tries to read or write to a memory address range that is outside of the allocated application memory space. The memory region assigned to an application is known as the process address space.

This style of memory management was available in 16-bit Windows but because of the method that 16-bit Windows used to simulate multitasking, it was possible to overwrite sections of memory that were owned by other applications or the operating system before the errant program crashed.

Depending on what portions of the operating system were overwritten, another (completely unrelated) program that had no errors in it could try to use a damaged operating system function, resulting in both a program and operating system crash.

The common symptom of this behavior was the infamous "blue screen of death" which told you something was wrong, but often reported misleading causes of the problem. If an application destroyed critical sections of the operating system, the result was often a "black screen of death" - an instant black screen accompanied by a solidly locked or frozen machine. This obviously gave no feedback as to the cause of the problem.

As improvements in hardware design occurred, the use of hardware based multitasking in 32-bit Windows made protected-mode memory managers increasingly reliable and resulted in new operating systems with ever increasing stability - more able to cope with preventing operating system crashes when an application crashes.

From this we can see that one of the fundamental "rules" of writing code for a protected mode operating system is to ensure the application code can read and write only within the process address space.

However, because Inline Assembler allows you to read and write to almost any memory address, this clearly places the onus on the programmer to take suitable precautions with all referenced memory addresses.

For example, if you allocate a 10 Kb buffer and subsequently try to read 20 Kb, the protected mode memory manager will trigger a Page Read fault (GPF) the instant the address goes outside the process address space - typically this would occur when the memory address advances beyond the original 10 Kb buffer.

An application can also get into similar trouble if it incorrectly dereferences a register (i.e., if it incorrectly treats the register content as a or address, rather than a value). If the address points outside of the allocated process address space, a GPF is virtually guaranteed.

Page read and write faults are exceptions (GPFs) that are passed from the operating system to the application that makes the error. If the exception is not handled by the application, the operating system closes the application. Current versions of PowerBASIC do not support native exception handling, but it is possible to configure an exception "trap" function using the Windows API.

Therefore, to create effective and stable assembler code, you should become familiar with protected mode programming concepts. As outlined above, you must never access memory not specifically assigned to your process, nor should you ever change the selector value in a segment register.

Likewise, all Calls, Jumps, and Returns should use "Near" addressing, as a full 32-bit offset is utilized. Should you violate any of the rules of protected mode programming, your code will likely fail catastrophically with a General Protection Fault (GPF).

See Also

The Inline Assembler

Flat memory model


Mnemonics and Operands

An assembly code instruction (statement) consists of a mnemonic (pronounced "nih-MON-ick"), and between zero and three operands. For a logical or arithmetic mnemonic with two operands, the right operand is the source and the left operand is the destination. In general, 80x86 assembly code instructions takes the following form:

[ASM|!] mnemonic destination, source

For example:

! MOV EAX, 1

ASM ADD EAX, EBX

In the examples above, the keywords MOV and ADD are the mnemonics, EAX is the destination operand, and 1 and EBX are the source operands.

In the first line, the value 1 is "moved" into the EAX operand (register). In BASIC code, this works similarly to the statement A = 1. The second example adds the value in EBX to the value in EAX and stores the result in EAX. In BASIC code, this works similarly to A = A + B.

See Also

The Inline Assembler

Using assembly-language in your code

Opcodes and Mnemonics

Registers


Opcodes and Mnemonics

At the hardware level in an Intel or compatible processor, instructions are built directly into the CPU circuitry and these are represented by opcodes.

While assembly code can be written at the Byte level, it is a particularly complex method of writing code since it involves memorizing a very large number of opcodes. Additionally, such program code must use the Intel numeric (little-endian) data format. With little-endian, multi-byte numeric values are stored in reverse order to usual human representation.

For example, to copy the 32-bit value &H56A700FE into the EAX register (MOV EAX, &H56A700FE), you must first find the opcode for the MOV EAX mnemonic (&HA1) followed by the data in reverse order (&HFE, &H00, &HA7, and &H56).

In hex format, the whole instruction would look like this:

A1 FE 00 A7 56

Obviously this becomes a very tedious and error prone way to write assembly code. As a result, a system was developed (many years ago) where groups of similar opcodes were given verbose names that made them a lot more convenient to use than raw numeric opcodes. These names are referred to as mnemonics, and this is the system used in PowerBASIC's 32-bit Inline Assembler.

Each mnemonic represents a reserved name that represents a family of opcodes that perform similar tasks in the processor. The actual numeric opcodes are different depending on the size and type of operands being used. For example, with the MOV mnemonic:

! MOV EAX, VAR1 ' opcode = &HA1

! MOV VAR1, EAX ' opcode = &HA3

The use of mnemonics provides a far more intuitive way of representing opcodes; however, there is no exact correlation between what you write using mnemonics and what you get as finished opcodes. This is because the opcode you actually get for a given mnemonic can depend on whether it is using near or far addressing, the operand data types (registers or pointers or constants), etc. However, PowerBASIC takes care of these details automatically and transparently for you, leaving you to get on with writing your actual program.

See Also

The Inline Assembler

Using assembly-language in your code

Mnemonics and Operands

Registers


Registers

Registers are a special working area within the processor. Registers are faster than memory operands, and are designed to work with the processor's opcodes.

Registers in an Intel or compatible processor are a very limited resource when writing assembler. In essence, there are eight general-purpose registers, EAX, EBX, ECX, EDX, ESI, EDI, ESP, and EBP. In most instances, ESP and EBP should remain unused as PowerBASIC uses them for entry and exit of procedures.

This means that you have six 32-bit registers to use in your assembly code, plus any other memory locations that are useful in the procedure. ESI and EDI can be used in the normal manner in most instances but neither can be accessed at a Byte level. You can read the low WORD of ESI as SI and the low WORD of EDI as DI.

Understanding the size of registers and the data that you can place in them is very important when using assembler. A 32-bit Intel or compatible processor has three native data sizes that can be used by the normal integer instructions, BYTE, WORD, and DWORD corresponding to 8-bit, 16-bit and 32-bit.

This can be shown in HEX notation.

BYTE 00

WORD 00 00

DWORD 00 00 00 00

In terms of registers, this corresponds to the three sizes that can be addressed with the normal integer registers. Intel and compatible processors are backwards compatible with older code that uses 8 and 16-bit registers, and it is done by accessing any of the general purpose registers in three different ways. Using the EAX register as an example:

AL or AH = 8 bit

AX = 16 bit

EAX = 32 bit

This is the schematic of a general purpose 32-bit register:

This schematic is easier to understand at a bit level. Reading from the right side, you have 32 bits in the register, bits 0 to 31. Because of the bit positions for each piece of data that can be accessed in a 32-bit register, AL is called the Low (low-order) byte, AH is called the High (high-order) byte and AX is called the Low word.

To read the first BYTE in the register (bits 0 to 7) you use:

! MOV byteval, AL ; Copy the low-order byte into variable

Likewise, to read the second byte in the register (bits 8 to 15) you use:

! MOV byteval, AH ; Copy the high-order byte into bytevar

If you want to read the first WORD in the register (bits 0 to 15) you use:

! MOV wordval, AX ; Copy the low-order Word into a variable.

To get at bits 16 to 31, you must rotate the bits in the register so they can be accessed by the previous instructions. Rotating a 32-bit register in either direction by 16 bits move the low-order 16-bits into the high-order 16-bit positions, and the high-order 16 bits into the low-order 16-bit positions of the register.

! ROL EAX, 16 ; Rotate EAX left by 16-bits

…or:

! ROR EAX, 16 ; Rotate EAX right by 16-bits

You cannot put a different size piece of data into a register than its correct size and you cannot mix different register sizes:

! MOV EAX, CL ; This fails as EAX is 32-bit, CL 8-bit

If you need to put the value in CL into a 32-bit register, you must first convert it using one of a number of different techniques:

! MOVZX EAX, CL ; Zero extend unsigned Integer

! MOVSX EAX, CL ; Sign extend signed Integer

In some instances you can use:

! XOR EAX, EAX ; Clear EAX

! MOV AL, CL ; Copy CL into AL

In addition, there are also some "older" mnemonics that will do the conversions too:.

! MOV AL, CL ; Copy CL into AL

! CBW ; Convert Byte in AL to Word in AX

! CWDE ; Convert Word in AX to DWORD in EAX

See Also

The Inline Assembler

Data types in registers

MMX registers

Saving registers

Saving Registers at the Sub/Function level

Tricks in preserving registers

Saving the FPU registers

Using ESP and EBP


Data types in registers

There are three basic types of operands that can be placed in a register: immediate, memory or another register.

An immediate operand is usually a numeric literal (number) but it can also be a string literal in the form "a" which is converted by PowerBASIC to its ASCII equivalent code.

! MOV AL, "a" ; String literal

! MOV EDX, 0 ; Numeric immediate/literal

A memory operand is an address in memory of some form of data:

! MOV AL, [ESI] ; Copy byte at address in ESI into AL

! MOV EDX, lpMemvar ; Copy variable address into EDX

A register operand is a register with a value in it:

! MOV ECX, EDX ; Copy EDX into ECX

The actions that can be performed are determined by the available opcodes. For example, trying to move one memory operand directly into another does not work because there is no opcode in the 80x86 processor to do it.

! MOV mVar, lpMem ; This fails as there is no opcode

However, if you have a "spare" register, you make an indirect copy through that register:

! MOV EAX, lpMem ; Copy memory value into register

! MOV mVar, EAX ; Copy register into memory variable

If you don't have a "spare" register, it can be done another way but it is slightly slower:

! PUSH lpMem ; Push memory value onto the stack

! POP mVar ; Pop it off as another memory value

See Also

The Inline Assembler

Registers

MMX registers

Saving registers

Saving Registers at the Sub/Function level

Using ESP and EBP

Saving the FPU registers


MMX registers

According to the Intel documentation, all MMX registers require parentheses around the register number. These were compulsory in early versions of PowerBASIC, but the parentheses are now optional.

! PXOR mm(7), mm(7) ' PB/DLL 5.0, PB/CC 1.0 format

! PXOR mm7, mm7 ' PB/DLL/WIN 6.0+, PB/CC 2.0+ format

See Also

The Inline Assembler

Registers

Data types in registers

Saving registers

Saving Registers at the Sub/Function level

Tricks in preserving registers


The stack

The stack is a range of memory addresses that can be used for temporary storage of data from either within a procedure or as the normal method of passing parameters to a procedure.

The stack is normally accessed in code by the mnemonics PUSH and POP. The stack is accessed on a last on, first off basis which means that the last value pushed onto the stack is the first one to be popped back off the stack.

The next position that can be written on the stack is called the top of the stack. When a piece of data is pushed onto the stack, the processor decrements the stack pointer ESP then writes the data to the top of the stack. When a piece of data is popped back off the stack, the processor reads the data from the top of the stack then increments the stack pointer. Therefore, the stack address decreases as more data is pushed onto it, and the address increases as data is popped back off the stack.

In the following images, each square represents 1 byte on the stack, and the different colors are intended to demonstrate the different data sizes being pushed and popped. The top of the stack is the left side of each image.

The following sequence demonstrates the stack layout as one 32-bit and two 16-bit values are pushed and popped from the stack.

Existing stack layout:

Pushes

Push a 32 bit value (PUSH EAX)

Push a 16 bit value (PUSH CX)

Push another 16 bit value (PUSH DX)

Pops

Pop a 16 bit value (POP DX)

Pop second 16 bit value (POP CX)

Pop the 32-bit value (POP EAX)

If you wrote the following code:

! MOV EDX, 100

! MOV ECX, 500

' push the 2 values onto the stack

! PUSH EDX ; EDX has the value 100

! PUSH ECX ; ECX has the value 500

' pop the 2 values off the stack

! POP EAX ; EAX has the value 500

! POP ECX ; ECX has the value 100

PowerBASIC conforms to the 32-bit Windows convention that specifies that certain registers must be preserved around blocks of assembly code, namely EBX, ESI, and EDI. While EAX, ECX, and EDX can be modified freely within a procedure, some conditions apply to their use too. See Saving registers for more detailed information.

See Also

The Inline Assembler

Balancing the stack

Tricks of the stack

Stack Overhead Reduction

Saving registers

Saving Registers at the Sub/Function level


Balancing the stack

An important consideration when using the stack is to be symmetrical in the byte count of what is pushed and what is popped.

If the stack is not balanced on exit from an assembly code block (i.e., you POP too few or too many registers), your routine will return to the wrong location in your code. This is because PowerBASIC must assume that the last item on the stack is the address to which it should return.

In other words, if the stack is not balanced on exit from an assembly code block, program execution is likely to resume at the wrong address and instantly crash the program. In most instances, if you PUSH a given data size onto the stack, you must POP the same data size.

See Also

The Inline Assembler

The stack

Tricks of the stack


Tricks of the stack

The stack is very flexible in what can be pushed and popped. There are a few tricks that are very useful when using the stack, you can push a 32-bit value and then pop two 16-bit values.

' Push a 32 bit value onto the stack

! PUSH EDX

' Now pop two 16 bit values off the stack

! POP AX

! POP CX

Even though the pushed data size is different to the popped data size, four bytes have been pushed onto the stack and four bytes have been popped back off the stack so the stack is balanced.

The stack can be used for many different things, you can push a register and pop it later when you need it so that you do not need to allocate a memory variable to put it in. You can use the stack to move a piece of data between memory operands and registers.

! PUSH ECX

! POP memVar

…or:

! PUSH memVar

! POP EDX

…or between two memory variables:

! PUSH memVar1

! POP memVar2

…instead of using a register:

! MOV EDX, memvar1

! MOV memvar2, EDX

A collection of small tricks of this type free up the number of registers that you can use in your , provided the stack is managed carefully.

Before the end of your routine, you should make sure that all the registers you have pushed onto the stack have also been popped from the stack. It is easy to make a mistake in this area, especially if the routine conditionally PUSHes and POPs any registers.

See Also

The Inline Assembler

The stack

Balancing the stack

Stack Overhead Reduction


Stack Overhead Reduction

There may be some instances where you wish to repeatedly call a very small SUB and this may produce a situation where the normally modest stack overhead may actually become a factor in the speed of the entire algorithm.

To help boost performance in such cases, PowerBASIC offers the often-overlooked GOSUB/RETURN statements, which can be used in place of a call to a SUB/END SUB block. Where stack overhead reduction is critical, you can create a Label in the code below the end of your normal code (but still within the current Sub/Function block).

You may then take the Inline Assembler code from the target SUB, and place it right after the Label. Finally, a RETURN statement is added so that execution resumes at the next instruction after the GOSUB. Such code would look something like this:

FUNCTION MyFunc() AS LONG

' Inline Assembler code

GOSUB LABEL

' More Inline Assembler code

EXIT FUNCTION ' or EXIT SUB

LABEL:

' Your Inline Assembler code

RETURN

END FUNCTION ' or END SUB

This technique is very efficient because the variables used in the Inline Assembler Sub/Function (that have been moved from a SUB, back into the calling code) are maintained within the same scope as the calling code, and can therefore be used without having to pass them on the stack. The result is that we have eliminated virtually all the stack overhead involved in repeatedly calling a SUB.

Finally, you can even use the standard Intel assembler notation !CALL and !RET within the Sub/Function, to jump to the Label and return from it to the next instruction. For example:

FUNCTION MyFunc() AS LONG

' Inline Assembler code

! CALL LABEL

' More Inline Assembler code

EXIT FUNCTION ' or EXIT SUB

LABEL:

' Your Inline Assembler code

! RET

END FUNCTION ' or END SUB

Finally, it is very important to note that you must NEVER exit a PowerBASIC Sub/Function with the RET instruction. PowerBASIC Subs and Functions automatically perform their own stack cleanup (of local variables, etc) when an END SUB/FUNCTION or EXIT SUB/FUNCTION statement is executed, whereas an RET instruction would try to force a Sub/Function exit without the internal stack cleanup being performed. A RET instruction cannot ever be used as a substitute for these BASIC statements.

In summary, your program will fail with a spectacular Stack Fault (GPF) if you attempt to terminate a PowerBASIC Sub/Function with RET mnemonic.

See Also

The Inline Assembler

The stack

Balancing the stack

Tricks of the stack

Saving registers


Saving registers

When writing assembler code in 32-bit Windows, there is a convention that governs the use of registers so programmers can interact with the Windows API functions in a predictable and completely reliable way.

However, the registers available with an 80x86 processor are a very limited resource, and they are used by every application (process) running and also by the operating system itself. Therefore, a reliable method of using registers is very important to the process of writing reliable assembler code.

An 80x86 processor has eight general-purpose integer registers, EAX, EBX, ECX, EDX, ESI, EDI, ESP, and EBP. Of these, ESP and EBP are almost exclusively used to manage the entry and exit to a procedure, so there are effectively just six general-purpose registers available for application level programming.

Following on, the Windows convention splits the remaining registers so that 3 can be freely modified (EAX, ECX, and EDX) within the Sub/Function that uses them, while the other 3 must be preserved (EBX, ESI, and EDI) by the Sub/Function. For the sake of this discussion, we'll refer to these two sets as scratch and volatile registers respectively.

In summary, PowerBASIC automatically preserves EBX, ESI, and EDI at the Sub/Function level, but the programmer is responsible for preserving both the scratch and volatile registers within the Sub/Function.

"Preserving the registers" does not necessarily mean that you must push all the registers on the stack, though that is the usual way of ensuring their safety. Simple routines might not modify any of the registers; in which case, you may not need to take any precautions. We use may because it's best to avoid making assumptions, especially with assembler programming. It is better to be safe than sorry. When in doubt, preserve (save and restore) all of the registers.

See Also

The Inline Assembler

Registers

Saving Registers at the Sub/Function level

Saving the FPU registers

Tricks in preserving registers


Saving Registers at the Sub/Function level

To conform to the Windows programming conventions, PowerBASIC must provide a "safe" environment for the range of functions that are available. This is achieved by transparently preserving the three volatile registers at the start of each Sub/Function, and restoring these same registers before exit from the Sub/Function. The following example shows approximately how PowerBASIC constructs the entry and exit of a Sub/Function to preserve these registers:

SUB MySub(Params)

! PUSH EBX ' Automatically added by PowerBASIC

! PUSH ESI ' --"---

! PUSH EDI ' --"---

' the actual SUB code is placed here

! POP EDI ' Automatically added by PowerBASIC

! POP ESI ' --"---

! POP EBX ' --"---

END SUB

When writing a Sub or Function in PowerBASIC, we can safely predict that the EBX, ESI, and EDI registers will be automatically saved upon entry and restored upon exit from a Sub/Function.

The virtue of code that observes these conventions is that it allows the programmer to safely assume that a call to any other Sub/Function or API function is certain to follow the same register preservation rules for the EBX, ESI, and EDI registers. This helps ensure that writing Inline Assembler code in PowerBASIC will result in reliable and completely predictable code execution in terms of register use when calling PowerBASIC and API Subs/Functions.

The PowerBASIC compiler is also very efficient in the way it calls API system functions. For example, the following BASIC statement which calls the SendMessage API:

CALL SendMessage(hWnd&, %WM_COMMAND, 50, 100)

…is translated into assembly code in the compiled program, to resemble something like this:

PUSH 100

PUSH 50

PUSH %WM_COMMAND

PUSH hWnd&

CALL SendMessage

This direct low level translation is one of the main reasons why PowerBASIC programmers can easily mix API code and assembler code. However, when it comes to intermixing assembler and BASIC code within a Sub/Function, the programmer must take additional care.

See Also

The Inline Assembler

Saving registers

Using ESP and EBP

Saving the FPU registers

Tricks in preserving registers


Intermixing ASM and BASIC code

There are special conditions with register preservation that apply when writing mixed assembler and BASIC code. PowerBASIC is a highly optimized compiler and among its optimizations are reductions in the stack overhead between BASIC code statements. Therefore, compiled PowerBASIC code is designed to expect that the EBX, ESI, and EDI registers will remain unchanged between lines of BASIC code.

This means that if your assembler algorithm uses any of the EBX, ESI, or EDI registers, you must preserve their original state from the last line of BASIC code that precedes the Inline Assembler code. This is, you must PUSH them before your ASM code, and POP them again right before the BASIC code commences.

This may appear to be more code than is necessary but it must be remembered that the internal structure of PowerBASIC does not duplicate the stack preservation that the application programmer must apply, so in terms of the stack overhead, the code is actually very efficient.

It should be noted that if your ASM code uses the EAX, ECX, or EDX registers, you should also preserve these as the internal execution of BASIC statements can also freely modify any of these three registers too.

The overall approach to preserving the registers around intermixed ASM and BASIC code is demonstrated in the following listing:

SUB TestProc(var1&, var2&)

#REGISTER NONE ' Ensure there is no conflict with

' PowerBASIC Register variables

' Code that uses EAX ECX and EDX goes here

...

! PUSH EAX ' Save the scratch registers

! PUSH ECX

! PUSH EDX

...

' Call an API function here

...

! POP EDX ' Restore the scratch registers

! POP ECX

! POP EAX

...

' Other ASM code that uses EAX ECX and EDX goes here

...

! PUSH EBX ' Save the volatile registers

! PUSH ESI

! PUSH EDI

...

' Other BASIC statements here, for example:

var1 = var2 + 2^8 - COS(var2)

...

! POP EDI ' Restore from the stack

! POP ESI

! POP EBX

...

' More ASM code that relies on EBX, etc

END SUB

Using this format ensures that you are writing "safe" code and that all of the utilized registers are preserved, because:

  • The EBX, ESI, and EDI registers are preserved by the PowerBASIC compiler at the Sub/Function level.

  • The EAX, ECX, and EDX registers are preserved by the application programmer around the API function call and the BASIC statements. This strategy ensures that EAX, ECX, and EDX registers are not overwritten (destroyed) by the function that is called.

With those points in mind, if there are no BASIC statements or API calls after the assembler code, preserving these registers is of no consequence. In this case, the automatic preservation code will take care of EBX, ESI, and EDI registers before the Sub/Function terminates, and we can be sure that the calling code will also preserve the EAX, ECX, and EDX registers using the same conventions.

PROGRAMMING TIP: As described above, the EBX, ESI, and EDI registers are automatically preserved at the start and exit of a Sub/Function. Therefore, if you need to use registers for counters or to store other values in your Inline Assembler code, you may use any of the EBX, ESI, or EDI registers for this purpose as they are restored when the Sub/Function terminates. This helps ensure efficiency and can result in even slightly faster code since we do not have to preserve extra registers each time the Sub/Function is executed.

See Also

The Inline Assembler

Registers

Data types in registers

MMX registers

Using ESP and EBP

Saving registers

Saving Registers at the Sub/Function level

Saving the FPU registers

Tricks in preserving registers


Using ESP and EBP

It is possible in PowerBASIC to write your own procedure within an existing Sub or Function by manually coding the stack entry and exit. This is a complicated area of assembler coding where it is very easy to crash the entire operating system if the code is not written correctly. For example:

! CALL procname

' Other PowerBASIC code here

! JMP label ; Jump over the procedure

procname:

! PUSH EBP ; Preserve base pointer

! MOV EBP, ESP ; Stack pointer into EBP

' Write your assembler code here

! MOV ESP, EBP ; Restore stack pointer

! POP EBP ; Restore base pointer

! RET

label:

...

' Other PowerBASIC code here

There are other methods of preserving both ESP and EBP depending on personal taste and calling conventions, but you must save and restore the states of both registers if you choose to use a procedure of this type. It is very important to note that ESP and EBP must always be preserved if they are to be altered, regardless of relative position of Inline Assembler code to BASIC statements.

See Also

Registers

MMX registers

Saving registers

Saving Registers at the Sub/Function level

Data types in registers


Local variables

When dealing with LOCAL variables in a Sub/Function, you have to manually calculate the stack offsets of both the parameters passed and the local variables that you use. Passed parameters start at [EBP + 8] and local variables are allocated at "negative" offsets on the stack, relative to EBP. To code something that would normally look like:

! MOV EAX, -var1 ; Invalid syntax

…you would have to write the line of code in a fully manual procedure, in the form:

! MOV EAX, [EBP + 8]

In this context, EBP is actually a memory operand and the displacement in either direction is the address of either a parameter or a LOCAL variable. We are adding 8 to the stack base pointer since the stack grows downward in memory, and we wish to access local variable storage that is allocated in memory above the base pointer.

See Also

The Inline Assembler

Addressing and pointers

Effective addressing


Saving the FPU registers

Whereas the CPU has registers with fixed names (EAX, etc), the FPU (Floating-Point Unit or co-processor) has stack-like registers which are numbered according to their position in the stack: ST(0) {top}, ST(1), ST(2), …, ST(7) {bottom}. You deal with the FPU by loading a value onto the top of the FPU stack, or by storing the value held at the top of the stack. The latter operation may or may not involve removing the value from the stack.

Note the term loading is used to describe placing a value on the FPU stack, yet it operates more like a PUSH operation. In PowerBASIC, it is not a question of which FPU registers are available, but that four registers (or stack slots) are usually available for use by the programmer. If more are required, the stack should be saved and restored accordingly.

FSAVE/FRSTOR

To preserve the entire FPU stack, the mnemonics FSAVE and FRSTOR take care of preserving, and restoring, the FPU stack (respectively). These work in a similar way to the PUSHFD and POPFD CPU, but are notoriously slow to execute and FPU programmers often avoid their use unless necessary. However, they can be useful when starting to write FPU code since they guarantee the preservation and restoration of the FPU stack.

See Also

The Inline Assembler

Saving registers

Saving Registers at the Sub/Function level

Using ESP and EBP

Tricks in preserving registers


Tricks in preserving registers

When you are developing mixed assembler/API code, and you do not know what registers are used in the API functions, you can draw upon two pairs of assembler instructions that preserve all of the usual registers and the CPU flags as well: PUSHAD, POPAD, PUSHFD, and POPFD.

PUSHAD/POPAD

The first pair of mnemonics, PUSHAD and POPAD, save and restore the registers in a block. These mnemonics allow you to do things like display the value of a register in the middle of assembler code with a MessageBox API call.

' ...Assembler code

! PUSHAD

var& = 0

! MOV var&, EAX

MessageBox hWnd&,BYCOPY STR$(var&),"Test Value",%MB_OK

! POPAD

' ...More assembler code

It should be noted that the use of PUSHAD and POPAD in release code is less-than-optimal code design. That is, it does more work than is usually needed, but in the development stage, these two instructions can be very convenient.

PUSHFD/POPFD

If the code being tested has certain instructions, such as conditional jumps that depend on flag states within the processor, the other pair of block instructions to utilize is likely to be PUSHFD and POPFD. These preserve the state of the processor flags while code that may modify the flags is executed.

PROGRAMMING TIP: If the STD instruction is used to set the CPU direction flag, a CLD instruction must be executed before releasing control to a Windows API function or a BASIC statement.

See Also

ASM statement

The stack


Addressing and pointers

An important distinction in assembler is the difference between the address of a variable and the value of a variable. The address of a variable is where it is located in memory; the value of a variable is what is stored at that address.

This is the ADDRESS of the variable in memory:

This is the VALUE at that address:

The method used in assembler to get the value at an address is a technique called dereferencing.

! MOV EAX, lpvar ; Copy address into EAX

! MOV EAX, [EAX] ; Dereference it

! MOV nuvar, EAX ; Copy EAX into new variable

Using square brackets around EAX gives access to the information at the address in EAX. This is the case with any 32-bit register. A register enclosed in square brackets is effectively a memory operand. The size of the data accessed at the address is determined by the size of the register used to receive it. In the above example, it would be a 32-bit value since it uses a 32 bit register for the destination operand. Naturally, it can be done with 8 and 16-bit values as well using the correct size register.

Pointers

Pointers are a useful high-level language abstraction for passing addresses between procedures and performing other types of complex data manipulation.

In assembler, when you use an instruction like:

! LEA EAX, MyVar

…you have put the address of a variable into the EAX register. When you take the next step and put that address into a variable of its own, you will have a POINTER to the address:

! LEA EAX, MyVar

! MOV lpMyVar, EAX

The mechanics of this process are worth understanding as it can generate errors that are hard to track down when the technique is used incorrectly.

You can pass a pointer to another procedure either by its value:

! MOV EAX, lpMyVar ; Copy the value into EAX

! PUSH EAX ; Push it as a parameter

! CALL MyProcedure ; Call the procedure

…or you can pass it by reference:

! LEA EAX, lpMyVar ; Load the address into EAX

! PUSH EAX ; Push it as a parameter

! CALL MyProcedure ; Call the procedure

When you pass an address in this manner, you have added an extra level of indirection so at the procedure end, you have a reference to a reference to an address. To get the address in the procedure, you need to dereference the variable to get back the original address:

! MOV EAX, lpMyVar

! MOV EAX, [EAX]

The original address is now stored in EAX.

See Also

The Inline Assembler

Effective addressing


Effective addressing

The notation to calculate the effective address of data in memory can look complicated but it is in fact very clear and precise notation. In the range of allowable notation for Intel 80x86 assembler, an address in memory can be placed in a register and treated directly as a memory operand by enclosing it in square brackets.

! MOV EAX, lpArray ; Copy address into EAX

! MOV ECX, [EAX] ; Copy 1st item in array into ECX

! ADD EAX, 4 ; Increment the array location by 4 bytes

! MOV ECX, [EAX] ; Copy 2nd item in array into ECX

This works fine in simple situations where the register that has the address is manually incremented or decremented by the data size each time it is accessed, but there is a much more powerful and flexible technique available by using the standard Intel notation that is available.

The Intel 80x86 allows the following format to calculate the effective address of a value in memory:

[ Base Address + Index * Scale + Displacement ]

Base Address The register that has the starting address or base address of the array (or buffer) in memory.

Index The register used to determine the offset from the base address.

Scale The data size based multiplier for the index.

Displacement The additional offset adjustment from the base address.

For example:

[EBX + ECX * 4 + 8]

EBX is the Base Address

ECX is the Index

4 is the Scale based on the data size

8 is the Displacement in BYTES

Not all of the additional notation has to be used. For example, in a Byte array, you can just use the base address and the index:

! MOV AL, [EBX + ECX]

The advantage of this technique is that you set the base address once and vary the index. In the case above, ECX is the index. In terms of flexibility, you have the choice of varying the base address, the index, and the displacement so that you can access data in memory by a number of different methods that best suit your code.

The only difference when using data sizes larger than Byte is that you multiply the "index" by the "scale" of the data size:

! MOV EAX, [EBX + ECX * 4]

To make a practical example let us assume we have an array of 64 items that were each 32-bits in size, and we wanted to read the 16th member of that 32-bit array. In this case, we would copy the 16th member of the zero-based index into the register that we are using as the index. Next, copy the address of the array into the register that you are using as the base address, and finally read the value of the array member into another register.

! MOV ESI, lpArray ; Base address register

! MOV ECX, 15 ; Zero-based index register

! MOV EAX, [ESI + ECX * 4] ; Copy the value into EAX

These three lines of code read the target value from the array into the EAX register.

If we wanted to compare the 16th and 17th members of the array and not have to use an additional register, we can add the required displacement so that we only have an extra line of code:

! MOV EAX, [ESI + ECX * 4]

! CMP EAX, [ESI + ECX * 4 + 4]

To compare the 17th and 18th members of the array, all we need to do is increment the index:

! INC ECX

Writing to the array is simply the reverse of reading it. With the same code as above:

! MOV ESI, lpArray ; Base address register

! MOV ECX, 15 ; Zero-based index register

! MOV EAX, 1234

! MOV [ESI + ECX * 4], EAX

We can also write an immediate (literal) number to the array but it takes a slightly different notation:

! MOV DWORD PTR [ESI + ECX * 4], 1234

The extra notation "DWORD PTR" is because there is no way for the assembler to determine the data size from either the memory operand for the array or the immediate number. Specifying the size tells PowerBASIC what data size should be written to the address contained in the memory operand.

A very similar notation is used when an array is placed on the stack by creating a LOCAL variable. With a stack variable MyArray, PowerBASIC resolves this variable to an address on the stack, which will be something like this:

[EBP -(displacement)]

You can use the standard Intel notation in the same way as with a register:

! MOV EAX, MyArray[ECX * 4]

…which is effectively:

! MOV EAX, [EBP - (displacement) + ECX * 4]

See Also

The Inline Assembler

Addressing and pointers

Registers

Passing parameters


Passing parameters

PowerBASIC 32-bit compilers pass all parameters to by pushing them in sequence from right to left. This is always the case when a procedure uses the default calling conventions of (and its synonym STDCALL), or the C calling conventions of . However, if the optional calling conventions are specified, parameters are pushed from left to right, and the called code is responsible for cleaning up the stack frame before returning. PowerBASIC Subs and Functions that use the BDECL convention automatically clean up the stack before returning execution to the calling code.

By default, PowerBASIC passes parameters by reference: a 32-bit to the data. You can also pass most parameters by value, by declaring with the optional keyword. When a parameter is passed by value, the actual value of the parameter is pushed on the stack.

Fixed-length strings, ASCIIZ strings, and User-Defined Types/Unions may also be passed as BYVAL or OPTIONAL parameters, now. Try to avoid passing large items BYVAL, as it’s terribly inefficient, and there is a maximum size limit of 64 Kb for a given parameter list. Arrays cannot be passed BYVAL.

PowerBASIC automatically sets up a local "stack frame" at the beginning of each Sub or Function in your program. As per standard conventions, the EBP register is used to address the parameters. The lowest parameter can be found at EBP+8, and subsequent parameters will be found in adjacent locations on the stack.

In assembler routines, it is easier and safer to access parameters by name rather than calculating their locations on the stack. However, it is important to remember the difference in accessing parameters passed by value and parameters passed by reference.

See Also

The Inline Assembler

Parameters passed by reference or by copy

Parameters passed by value

Passing arrays

Passing dynamic strings

Accessing PowerBASIC variables by name


Parameters passed by reference or by copy

When a parameter is passed by reference (the default method), PowerBASIC passes a 32-bit on the stack. That pointer is the actual 32-bit offset, or memory location, of the variable to be utilized as a parameter. A 32-bit pointer occupies exactly four bytes of stack space. A parameter passed by reference is typically accessed in this way:

SUB MyProc(xyz&) ' This will increment the

' parameter variable by one

! PUSH EBX

! MOV EBX, xyz&

! INC DWORD PTR [EBX]

! POP EBX

END SUB

Parameters passed by copy (such as expressions or constants) also take precisely 4 bytes on the stack. In this case, just as in parameters by reference, the item on the stack is not the value of the parameter. Rather, it is the address of a temporary location in memory where the value is stored. This may seem roundabout, but it has two distinct advantages. First, assembler routines can handle parameters and in precisely the same way. Second, routines can modify the value of a parameter without altering the original variable in the main program.

Suppose the first and only parameter is a Long-integer. In that case, you can put the integer value into the ECX register by writing:

SUB MySub(xyz&)

! PUSH EBX

! MOV EBX, xyz& ; EBX is a pointer to xyz&

! MOV ECX, [EBX] ; ECX now contains xyz&

' ...more code would go in here

! POP EBX

END SUB

In these cases, you must use the correct and complete address to access the value. But regardless of whether the parameter represents a variable, an expression, or a literal constant, or whether it was passed by reference or by copy, the routine will always work correctly.

See Also

The Inline Assembler

Passing parameters

Parameters passed by value

Passing arrays

Passing dynamic strings


Parameters passed by value

When a parameter is passed by value, the actual value is passed on the stack. The amount of space occupied will vary, depending upon the data type as indicated in the following table. A parameter passed by value is usually accessed in this way:

SUB MySub(BYVAL xyz&)

! MOV ECX, xyz& ; ECX now contains xyz&

' ...more code would go in here

END SUB

Argument type | Size on the stack (in bytes)
Byte, Word, Integer | 4
Dword, Long, Single | 4
Quad, Double, Currency, Extended Currency | 8
Extended-Precision Float | 12
Dynamic String | 4

See Also

The Inline Assembler

Passing parameters

Parameters passed by reference or by copy

Passing arrays

Passing dynamic strings

Accessing PowerBASIC variables by name


Passing arrays

Each array in your program has an associated array descriptor. This descriptor is saved in a proprietary format, which may change from version to version of PowerBASIC. Since most of the information in the descriptor in not relevant to assembler code, it is usually best to simply pass a to the first element of the array instead. You can use the VARPTR function to retrieve that address. Subsequent elements of the array will immediately follow the first in memory, while multi-dimensional arrays are stored in column-major order.

In addition to the LBOUND and UBOUND functions, the ARRAYATTR function can be used to obtain array attributes and information on a given array.

See Also

The Inline Assembler

Passing parameters

Parameters passed by reference or by copy

Parameters passed by value

Passing dynamic strings

Accessing PowerBASIC variables by name


Passing dynamic strings

A dynamic string variable is defined as a 32-bit data item, which contains a (or offset) to the string characters. When passed by value, the parameter is actually a 32-bit offset of the data. When passed by reference or by copy, the parameter is a pointer to another pointer that contains the offset of the actual string data. A dynamic string passed by reference is usually accessed in this way:

SUB MyProc(abc$)

! PUSH EBX

! MOV EBX, abc$ ; EBX is a pointer to the string handle

! MOV EBX, [EBX] ; EBX is now a pointer to string data

! MOV AL, [EBX] ; AL contains the 1st char of the string

' more code could go here

! POP EBX

END SUB

If you need to determine the current length of a dynamic string, there are two ways to do so. The end of string is always followed by a nul, CHR$(0), so it is possible to scan the string for the first occurrence. Of course, this will only work if there are no embedded nul bytes in the string data. An alternative method is to read the 32-bit Long-integer that immediately precedes the start of the string data, as the current length is always stored there.

PowerBASIC also calculates string literals in reverse order, in keeping with standard assembler operation. For example:

FUNCTION ab(x???) AS DWORD

! PUSH EBX

! MOV EBX, x???

! MOV DWORD PTR [EBX], "ABCD"

! POP EBX

END FUNCTION

The above code stores the value &H41424344 in the DWORD variable x, passed from the calling code. However, since the Intel platform uses little-endian numeric data format, the actual bytes are written to memory in the reverse order. For example, if we were to call the code above, and examine the actual memory locations of the passed parameter after the function call, we can see the effect of the reverse memory storage:

DIM a AS STRING, x AS DWORD

CALL ab(x)

a = HEX$(x,8) ' a = "41424344"

a = PEEK$(VARPTR(x),4) ' a = "DCBA"

See Also

The Inline Assembler

Passing parameters

Parameters passed by reference or by copy

Parameters passed by value

Passing dynamic strings

Accessing PowerBASIC variables by name


Accessing PowerBASIC variables by name

All variables in a PowerBASIC module are visible to Inline Assembler code created with the ASM statement. You can reference LOCAL, STATIC, GLOBAL, and THREADED variables by name. You can also reference procedure parameters by name, though you must differentiate between parameters passed by reference (BYREF) or by copy (BYCOPY), and those passed by value (BYVAL). Any variable referenced in an assembly-language statement must be defined prior to use.

SUB DoStuff (BYVAL c&)

LOCAL a%, b$

a% = 7 ' Local variable a%

! PUSH EBX

! MOV AX, a% ; Move value to AX

! ADD a%, AX ; Add value back to a%

b$ = "LINDA" ' Local variable b$

! MOV EBX, b$ ' Address of b$

! MOV [EBX], "l" ' Put lowercase "l" in first position

! MOV EAX, c& ' Put c& into EAX

! INC EAX ' Increment its value

! MOV c&, EAX ' Put it back

! POP EBX

END SUB

See Also

The Inline Assembler


Commenting Assembly code

On assembly code lines, a semi-colon ( ; ) is typically used for comments, although an apostrophe ( ' ) is still valid. For example:

SUB KerPlunk

ASM PUSH EBX ; Save EBX

ASM MOV EAX, 5 ; Put 5 into EAX

! MOV EBX, &HFF ; Put FFh into EBX

! ADD EAX, EBX ' EAX = EAX + EBX

! POP EBX ' Restore EBX

END SUB

See Also

The Inline Assembler


COM Programming Introduction

Welcome to PowerBASIC's newest addition to the PowerBASIC programmer's arsenal:

Component Object Model (COM) programming

PowerBASIC now supports the creation of COM client and controller applications. This section takes you on a guided tour of COM programming with PowerBASIC, starting off with discussions on COM theory and terminology, and proceeding to take you through Interface programming techniques used to create COM client applications.

These example applications will demonstrate how easy it is to manipulate a COM server application such as Microsoft Word™, and show how you can write simple routines to export data from your application to these Microsoft Office applications.

By the end of this section you should be able to start writing your own COM client applications, and add a whole new range of functionality to your existing PowerBASIC applications.

See Also

The PowerBASIC COM Browser

COM is an acronym?

Ok, but what actually is COM?

What is a COM component?

COM servers and COM clients

What is a Dispatch Interface?

Is there a Method to the madness?

COM initialization

Early-binding and Late-binding

Creating an Instance of an Object

Creating an early-bound object variable

Creating a late-bound object variable

How does the LET statement work?

New or pre-loaded?

Under the hood

Methods and Properties

Parameters

Positional and Named parameter passing

Return data

Enumerating collections

Converting COM code to PowerBASIC

Example COM controller application

COM Programming Summary


COM is an acronym?

Since its inception some years back, the term COM has been quoted (and misquoted) in a wide variety of situations, so our primary task is to define it more precisely. First off, the acronym "COM" stands for "Component Object Model". For the purposes of this chapter, we'll just be using the acronym COM when we refer to the technology as a whole, in the context of programming with PowerBASIC.

As far as the history goes, COM itself has had a colorful evolution as it has grown from OLE (Object Linking and Embedding), through VBX and OCX, to the more recent definitions of COM and ActiveX, as the design concepts have evolved. Given the history, many of these terms tend to be used interchangeably, leading to general confusion. For example, the term ActiveX is intended to describe specific types of COM components (for example, visual control components), but it is common for the term to be applied to all types of COM components. In this document, we will be limiting discussion to COM and how it applies to PowerBASIC.

See Also

COM Programming Introduction

The PowerBASIC COM Browser


Ok, but what actually is COM?

At the base level, COM is simply a set of rules that define how a COM-aware application and a COM component can communicate with one another. One of the fundamental concepts of COM programming is that the actual programming language(s) used to create the application and/or the component are completely unimportant to each other. COM just defines the underlying mechanism for the two to work together.

To make it even easier for the programmer, PowerBASIC encapsulates most all of the low-level details of the actual COM communication process, providing a straightforward way to load and communicate with a COM component using BASIC syntax.

However, in order to fully exploit a COM component, some understanding of the concepts and terminology of COM is essential, and we'll discuss these in turn.

See Also

COM Programming Introduction

What is a COM component?

COM servers and COM clients

What is a Dispatch Interface?


What is a COM component?

At this point, we start adding a whole host of new terminology into the discussion, and we'll explain these terms in more depth as we go, but for the time being, if any are unclear, please just accept the terminology being used and move forward.

Firstly, a COM component is commonly referred to as an Object or COM Object and these terms may be used interchangeably. There are several types of Objects, but for this discussion, we are only interested in COM servers or automation servers.

From the programmer's perspective, we can visualize a COM component or Object as simply a "black box" that comprises a set of functions and associated data. Internally, these Objects usually contain one or more pieces of reusable code (functions), and provide ways for an application to call the object's functions and read/write its associated data through its Dispatch Interface.

These components usually take the form of an EXE, or DLL/OCX file, but the actual file extension is largely irrelevant. However, DLL/OCX versions of a component are generally referred to as "in-process" since they are loaded into the address space of the calling application, whereas EXE-versions are typically "out-of-process" because they will not share the address space of the calling application.

Technically speaking, an Object is a special form of code library (similar to a standard DLL) that conforms to the COM specification, provides at least one Dispatch Interface, and is identified by a globally unique PROGID and CLSID.

A PROGID is a unique alphanumeric name representing a CLSID of a COM Object (for example "SourceSafe.0"), and the CLSID is a globally unique 128-bit binary "string" identifying the same Object. These identification strings are stored in the Windows Registry when the COM component is installed. PowerBASIC programmers reference COM components by their PROGID string, and rarely by their CLSID. However, since these are stored in pairs, it is straightforward to retrieve the matching PROGID for a known CLSID, and vice versa.

Yet thanks to COM, we do not really need to know how a COM Object does its job or is internally identified and initialized, we need only be concerned with what the job of the Object is, and how to communicate with the Object from the outside. The internal workings are effectively "hidden" from the outside world, and this is referred to as encapsulation.

Since we have no ability to "look inside" a COM Object, it is not (directly) possible to manipulate internal variables or memory, so a COM Object provides a degree of security for its internal code. The programmer can only work with the functions and associated data the COM Object provides externally. In practice, this is very similar to the use of a standard DLL that contains a few exported routines, yet contains a great many private routines.

The next part of this topic compares the roles of the server and the client.

See Also

COM Programming Introduction

Ok, but what actually is COM?

COM servers and COM clients


COM servers and COM clients

A COM server is an instance of a COM Object acting as an automation server. This server is subsequently controlled by an application known as a COM client or COM controller.

The client sends commands (requests) to the server and accesses its Properties through the server's Dispatch Interface. The COM server responds to the requests and processes them accordingly.

See Also


What is a Dispatch Interface?

The Dispatch Interface of a COM Object is another key COM definition. Broadly speaking, a Dispatch Interface is the actual mechanism that is used by a COM-aware application to communicate with the COM Object. In other words, an application communicates with a COM Object through its Dispatch Interface.

A Dispatch Interface can be represented in a similar manner to a User-Defined Type (UDT), but instead of member variables, an Object's Dispatch Interface members comprise a set of Functions and associated data. See INTERFACE/END INTERFACE for more details of the actual syntax of an Interface representation, along with The PowerBASIC COM Browser for a tutorial on examining the interfaces of objects and generating an interface definition file.

A COM Object may offer an unlimited number of Interfaces, but may expose only one Dispatch Interface. Internally, these Interfaces are exposed through the single Dispatch Interface.

A COM Object must support a Dispatch Interface to be used by PowerBASIC (and Visual Basic). PowerBASIC does not currently support the use of Direct Interfaces. Further, a PROGID and a CLSID must be present to uniquely identify each Dispatch Interface exposed by a COM Object.

See Also

COM Programming Introduction

Creating an early-bound object variable

The PowerBASIC COM Browser


Is there a Method to the madness?

As we have come to expect, COM uses its own terminology to describe the member functions and associated data of a Dispatch Interface. A member function is formally referred to as a Method, and the associated data is deemed a Property. Therefore, an application can call the Methods of an Object, and read/write the Properties of an Object.

There are three Property procedures available to manipulate an Interface Property: GET, LET and SET. Methods are initiated with a CALL. We'll describe each of these in more depth as we progress through the COM programming section.

See Also

COM Programming Introduction

COM initialization


COM initialization

Single-threaded and multiple-threaded EXE modules that use COM can leave PowerBASIC to handle all initialization and uninitialization requirements for each thread. This also applies to DLL's created with the PowerBASIC for Windows compiler.

However, in some instances, it may be necessary to explicitly uninitialize the COM subsystem for a given thread. For example to force a COM Object to unload from memory when it is no longer in use. In such cases, an application may explicitly call the CoUninitialize API function within that thread. However, if COM services are subsequently required again by that same thread, the COM subsystem must be reinitialized by a call to CoInitialize.

See Also

COM Programming Introduction

The PowerBASIC COM Browser


Early-binding and Late-binding

There are two ways to create an instance of a COM Object: Early-binding, and Late-binding. PowerBASIC supports both techniques.

Late binding, the simpler method, offers the most flexibility. You don't need to declare Methods, Properties, and parameters in advance, as everything is resolved at run-time (as the program is executed). Late-binding takes advantage of the fact that a COM Object knows a lot about itself, including information about all of its member functions. However, for that very reason, the validity of these references is not checked at compile-time. Virtually everything is done at run time.

Comparatively, early-binding requires that the compiler is made aware of all of the Interface's Methods and Properties that the COM client uses. This is achieved through the use of an INTERFACE/END INTERFACE block to define each Dispatch Interface. Unless this definition is present, PowerBASIC will be unable to validate the Interface members.

Late-binding, while slightly easier to code for, places more responsibility on the programmer to correctly specify the Interface members as they are used within the program. Additionally, since the actual Interface and member validation occurs only at run-time, performance will be slightly reduced as compared to early-binding. Therefore, taking the time to define an Interface and its members so that early-binding can be used is often worthwhile in order to gain a performance advantage at run-time.

However, manually coding an INTERFACE block can be a tedious job, and PowerBASIC comes to the rescue here with the new PowerBASIC COM Browser utility. The PowerBASIC COM Browser is designed to examine a COM component and generate all of the Dispatch Interface definitions and also any equates that can assist with passing parameters.

With the aid of the PowerBASIC COM Browser, PowerBASIC makes the task of implementing early-binding very simple.

See Also

COM Programming Introduction

The PowerBASIC COM Browser

Early Binding Tutorial

Late Binding Tutorial

Creating an Instance of an Object

Creating an early-bound object variable

Creating a late-bound object variable

How does the LET statement work?

New or pre-loaded?


Creating an Instance of an Object

In order to create an Instance of an Object, a new type of variable is introduced to PowerBASIC. These are known generally as object variables, and may only contain a reference to a COM Object, and no other value of any kind.

An object variable must be declared using its specific class. This could be the generic DISPATCH class, or one that you explicitly define with an INTERFACE structure. An object variable may only be used in specific situations, such as execution of an Object's Method. It is never legal to reference an object variable in normal numeric or string expressions, nor is it possible to even display or retrieve its value without the use of the special new functions like OBJPTR.

With these points in mind, we'll now examine how to declare an object variable for both early-binding and late-binding. In these examples, we'll also demonstrate how to create an instance of the target COM server, and place the reference in the object variable.

See Also

COM Programming Introduction

The PowerBASIC COM Browser

What is a Dispatch Interface?

Creating an early-bound object variable

Creating a late-bound object variable


Creating an early-bound object variable

An early-bound object variable must be declared using a Dispatch Interface definition block. Like a User-Defined Type declaration, these must be located outside of a Sub/Function, and before any reference to the block. For example:

INTERFACE DISPATCH WordApplication

MEMBER GET ActiveDocument<&H3>() AS WordDocument

MEMBER GET Name<&H0>() AS STRING

MEMBER GET Documents<&H6>() AS WordDocuments

...

END INTERFACE

FUNCTION PBMAIN

DIM oWord AS WordApplication

LET oWord = NEW WordApplication IN "Word.Application"

...

END FUNCTION

See Also

COM Programming Introduction

The PowerBASIC COM Browser

Creating a late-bound object variable

What is a Dispatch Interface?

How does the LET statement work?

Early-binding and Late-binding


Creating a late-bound object variable

Declaring a late-bound object variable is pure simplicity - we only need to declare it as a DISPATCH type and no INTERFACE blocks are required to be defined. For example:

FUNCTION PBMAIN

DIM oWord AS DISPATCH

LET oWord = NEW DISPATCH IN "Word.Application"

...

END FUNCTION

In both cases above, and assuming the Object could be initialized, the object variable oWord contains the Dispatch Interface reference to the Object. The LET statement may not set the ERR system variable if any run-time problems are encountered during the initialization of the Object, so you should use the ISOBJECT function to check state status of the object variable oWord.

See Also

COM Programming Introduction

The PowerBASIC COM Browser

Creating an early-bound object variable

What is a Dispatch Interface?

How does the LET statement work?

Early-binding and Late-binding


How does the LET statement work?

From the programmer's perspective, the operation of the LET statement is very straightforward - you nominate an object variable, the Interface name (or the generic DISPATCH type), and the ProgramID (registered Object name). From there, the LET statement initializes the Object and places the reference in the object variable.

This immediately raises an interesting question - how does the programmer know the Interface name and the ProgramID?

Simple: you can either get the information directly from the COM component, for example, using the PowerBASIC COM Browser or another Object viewer such as OLEVIEW. Alternatively, you could obtain the information from the documentation for the component from the author or distributor of the component.

For example, Microsoft provides a substantial amount of documentation on COM automation for products such as Word™, Excel™, etc. Obtaining such documentation for the COM component is often necessary, since just being able to create an instance on an Object is not of much practical use unless you can "drive" the Object too. Such documentation is often included with the component itself, and should also be obtainable from the manufacturer or distributor.

See Also

COM Programming Introduction

What is a Dispatch Interface?

INTERFACE/END INTERFACE block


New or pre-loaded?

You may have noted that the LET statement in the above examples included the NEW keyword. This tells PowerBASIC to request a new instance of the Object, rather than creating a reference to an existing instance. This distinction can be important. Let's imagine that you are creating a new instance of a component that produces its own GUI window. Microsoft Word™ and many other EXE-based COM servers typically work this way.

Therefore, when using the NEW clause, a new instance of Microsoft Word is created regardless whether an instance of Word is already running. That is, you could end up with two or more copies of Word running at the same time on the same PC.

In some situations this effect may be desirable, but there are also occasions where is it not. In such situation, you should first try to create a reference to an existing instance, and if this fails, you can then try to create a new instance. The general form of such code could look like this:

LET oWord = WordApplication IN "Word.Application"

IF ISFALSE ISOBJECT(oWord) THEN _

LET oWord = NEW WordApplication IN "Word.Application"

Or you could even use structured error handling:

TRY

LET oWord = WordApplication IN "Word.Application"

IF ISFALSE ISOBJECT(oWord) THEN ERROR 5

CATCH

LET oWord = NEW WordApplication IN "Word.Application"

IF ISFALSE ISOBJECT(oWord) THEN _

EXIT FUNCTION ' MSWORD not installed or launchable?

END TRY

Finally, using the NEW keyword in order to create a new instance of the Object may add additional overhead - the Object might require a significant amount of time to start up, but this is obviously very dependent on the Object. For example, the Windows Shell object is significantly faster to start than Microsoft Excel.

See Also

COM Programming Introduction

Under the hood

Methods and Properties


Under the hood

So far, we have seen that with a mere handful of lines of BASIC code, we can create an instance of an Object ready to interact with. While this appears to be a very simple procedure, PowerBASIC is doing an enormous amount of work for us behind the scenes. The following discussion is optional reading, but is recommended to give a perspective on what happens under the hood when creating a reference to an instance of a COM Object. It should be noted that this is a simplistic overview, since the actual implementation is very complex and beyond the scope of this document.

At the beginning of the process to create a reference to an Object, PowerBASIC communicates with the Object through an Interface in the Object called the Unknown Interface (IUnknown). All COM Objects must contain this Interface.

The IUnknown Interface is considered as the "building block" Interface of all COM components, and this is inherited by all other Interfaces provided by a COM component. Using a lollipop diagram, we can demonstrate how the intrinsic IUnknown Interface is represented:

You may also recall we mentioned earlier that a COM Object knows a lot about itself - it is through this IUnknown Interface that an Object provides details about itself to it controller (the COM client application). Naturally, PowerBASIC also makes use of this Interface to query the availability of IDispatch and other Interfaces.

If the COM component can provide an IDispatch Interface for late-binding purposes, PowerBASIC continues to initialize the component; otherwise, the component is released.

The lollipop diagram for an Object that supports the IDispatch Interface is represented like this:

If PowerBASIC obtains a reference to the IDispatch Interface, PowerBASIC retains the reference. In the case of late binding, this reference is placed in the DISPATCH object variable. All subsequent late-bound Interface calls are channeled through this IDispatch reference, first to obtain a reference to the target Method or Property, and then to access the actual target member (through a single entry-point called Invoke) using the reference ID obtained from IDispatch.

In the case of early-binding, PowerBASIC can skip the IDispatch Interface to query Interface, Methods, and Properties of the Object. This is because the target member reference (ID) is specified in the INTERFACE block (see the early-binding example above), so PowerBASIC can access the target Interface member through Invoke, without first asking for the reference through IDispatch.

To explain it a different way: with early-binding, PowerBASIC can skip the initial Interface and Interface member query at run-time because the relevant ID's for the target Interface and members have been predetermined during compilation instead. This results in a much-improved run-time performance, as compared to late-binding.

If an Object supports an Interface named "IDatabase", the lollipop diagram for that object can be represented like this:

Once PowerBASIC has an Interface reference, PowerBASIC application code is guaranteed access to all Methods and Properties (members) of that Interface. We'll demonstrate how to use these Interface members in the next section.

See Also

COM Programming Introduction

What is a Dispatch Interface?

Early-binding and Late-binding

Methods and Properties


Methods and Properties

A Dispatch Interface consists of a group of members deemed Methods and Properties. A Method is very similar to a normal function - it may take parameters and may return a result. A Property is more like a variable - it can hold a value or state.

A single Dispatch Interface can contain one or possibly hundreds of individual members.

PowerBASIC allows full access to Dispatch Interface members (Methods or Properties); however it is not possible to access a Property value directly. Rather, reading or writing a Property value has to be performed thorough the appropriate Interface Property member. This is similar to making a normal function call.

While this seems unusual compared with normal BASIC programming, it is actually an important principle of COM - only the Object itself can manipulate the data held within the Object's internal variables. This guarantees that the Object itself is wholly responsible for the movement of data in and out of the Object, providing an additional layer of security.

In addition to using the Property functions provided by the Interface, all return results and parameters passed from/to a Property (and a Method) must be performed using VARIANT variables.

Since Variants can be used to hold any type of data, from bytes to string arrays, they are the most convenient way to pass data between an application and a COM Object. Additionally, COM uses Unicode format for all data, but PowerBASIC automatically handles the translation between ANSI and Unicode for us.

With these rules in mind, let's use some real code to demonstrate accessing Interface Methods and Properties exposed by Microsoft Word™.

The following code performs a number of steps. It starts off by attempting to reference an existing instance to Microsoft Word™ or starting a new instance if there is no existing one. Next, it counts the number of documents open in Word, adds a document, places some text in the document, saves the document, and finally, closes the document and Word itself.

The code uses early-binding, but the actual INTERFACE definitions are not shown due to their relatively large size. The #INCLUDE file used to compile this code was generated directly by the PowerBASIC COM Browser and saved as a .INC file. The code also uses the optional LET statement for the purposes of clarity, but this is not compulsory.

#COMPILE EXE

#INCLUDE "oword.inc"

FUNCTION PBMAIN()

DIM oWordApp AS WordApplication ' Application Interface

DIM oWordDoc AS WordDocument ' Document Interface

DIM oWordSel AS WordSelection ' Selection

DIM vBool AS VARIANT

DIM vText AS VARIANT

DIM vFile AS VARIANT

DIM vFileFmt AS VARIANT

DIM vVnt AS VARIANT

DIM sResult AS STRING

' Open an instance of MSWORD

LET oWordApp = WordApplication IN "Word.Application"

IF ISFALSE ISOBJECT(oWordApp) THEN _

LET oWordApp = NEW WordApplication IN "Word.Application"

' Could MSWORD be opened? If not, terminate this app

IF ISFALSE ISOBJECT(oWordApp) THEN EXIT FUNCTION

' Make MSWORD visible

LET vBool = 1

OBJECT LET oWordApp.Visible = vBool

' Get the current document count from the "documents collection"

LET vVnt = 0

OBJECT GET oWordApp.Documents.Count TO vVnt

sResult = "MSWORD has " + FORMAT$(VARIANT#(vVnt)) + _

" Files loaded!"

' Add a document to the "documents collection", and obtain a reference

' to the document Interface for the new document

LET vVnt = 0

OBJECT CALL oWordApp.Documents.Add TO vVnt

LET oWordDoc = vVnt

' Get the current document count from the "documents collection"

' This should be one value higher than before

LET vVnt = 0

OBJECT GET oWordApp.Documents.Count TO vVnt

sResult = "MSWORD has " + FORMAT$(VARIANT#(vVnt)) + _

" Files loaded!"

' Get the Interface to the correct selection of the document

OBJECT GET oWordApp.Selection TO vVnt

LET oWordSel = vVnt

' Enter some text into the document at the selection position

LET vText = "PowerBASIC controls Microsoft Word!"

OBJECT CALL oWordSel.TypeText(vText)

' Set a paragraph marker (end of paragraph)

OBJECT CALL oWordSel.TypeParagraph

' Another paragraph

LET vText = "COM client controls a COM server!"

OBJECT CALL oWordSel.TypeText(vText)

OBJECT CALL oWordSel.TypeParagraph

#IF %DEF(%PB_CC32)

PRINT "Press a key to continue the demo"

WAITKEY$

#ELSE

MSGBOX "Click to continue the demo", &H00002000& ' = %MB_TASKMODAL

#ENDIF

' Save the new document to disk

LET vFile = "Test.doc"

LET vFileFmt = %wdFormatDocument

OBJECT CALL oWordDoc.SaveAs(vFile, vFileFmt)

' Close the current document and then close MSWORD completely

OBJECT CALL oWordApp.ActiveWindow.Close

OBJECT CALL oWordApp.Quit

' Close all of the Interfaces

LET oWordSel = NOTHING

LET oWordDoc = NOTHING

LET oWordApp = NOTHING

END FUNCTION

As can be seen, this code performs minimal error testing or validation of the Interfaces returned. However, it still clearly shows some important concepts. For example, it clearly shows how the return result from several different Properties can return Interface references in the Variant TO variables. In these cases, the code assigns the references to object variables so that the returned Interfaces can be used with further OBJECT statements.

See Also

COM Programming Introduction

The PowerBASIC COM Browser

Parameters


Parameters

This topic discusses passing parameters to Interface members using both Positional and Named Methods.

Parameters, assignment values, and return values must be passed as Variant variables.

Passing parameters to an Interface Method or Property is performed in a similar way to conventional parameter passing techniques - the terminology is really just a little different, and with COM, everything is done using Variant variables.

First, let's look at the syntax used to specify parameter-passing modes for Interface Methods and Properties, and examine the various options and their effects on the parameters. The actual parameter portion of a member definition are shown in bold in the following syntax definition block:

MEMBER {CALL | GET | SET | LET} membername <dispid> ( [[OPTIONAL [IN | OUT | _

INOUT]] paramname <dispid> [AS type] [,...]] ) [AS {vartype | interface}]

The IN, OUT and INOUT clauses work similarly to the and parameter passing modes. That is, the Object will not modify parameters marked as IN, but the Object can alter parameters marked as OUT or INOUT. The easiest way to remember this is to remember that the Object can output data through the parameter.

To set the value of a Variant variable to pass to an Interface member, simple assign the value to the Variant as is done with any other standard data type, or use the LET statement. For example, the two assignment lines in the following code are equivalent:

DIM vVnt AS VARIANT

...

LET vVnt = "PowerBASIC!"

vVnt = "PowerBASIC!"

It should be noted that passing an uninitialized Variant as a parameter to an Interface member is not recommended, since the Interface member may not be designed to cope with it. It is recommended that Variants at least be set to zero ( assignment of 0) or null ( assignment of an empty string) before passing the Variant parameter.

The AS type clause specifies the actual type of data that is stored in the Variant variable passed as a parameter. For example, if the parameter is of type STRING, the Variant parameter variable should contain string data.

If the OPTIONAL clause is present, the parameter can be omitted from the parameter list.

See Also

COM Programming Introduction

Positional and Named parameter passing

Return data


Positional and Named parameter passing

Finally, parameters can be passed in two ways: Positional and Named mode. Positional parameters work just like conventional parameter passing - the actual order of the parameters being passed must agree with the declared order in the INTERFACE member block.

However, using Named parameters offers a whole new way of specifying parameters. With Named parameters, code takes on an almost "self-documenting" parameter format, and since parameters are explicitly named, they can be specified in any order.

This is best explained by example. The following two OBJECT CALL statements are equivalent; however, the first OBJECT CALL statement uses positional passing, whereas the second uses named parameter passing.

INTERFACE DISPATCH IVSSDatabase

MEMBER CALL Open<&H1>(OPTIONAL IN SrcSafeIni AS STRING<&H0>, OPTIONAL IN Username AS STRING<&H1>, OPTIONAL IN Password AS STRING<&H2>)

END INTERFACE

...

' Positional parameter passing

OBJECT CALL oVss.Open(vIni, vUser, vPassword)

' Named parameter passing

OBJECT CALL oVss.Open(SrcSafeIni=vIni, Username=vUSER, Password=vPassword)

As mentioned above, Named parameters can be specified in any order. For example, the following statement is also equivalent to the two previous statements:

OBJECT CALL oVss.Open(Username=vUSER, Password=vPassword, SrcSafeIni=vIni)

It is also possible to mix Positional and Named parameters in a single OBJECT statement, but positional parameters must precede all Named parameters. It must be noted that not all COM Objects support Named parameters, but most do accept Named parameters.

Care should be exercised when reallocating or clearing Variant variables that have been used as parameters passed to a COM Object, since asynchronous execution in COM Object could require the Variant data be present for some duration or operation.

This behavior largely depends on the design of the COM Object; however, unexpected General Protection Faults (GPFs) in the COM Object can occasionally be traced to inopportune reallocating or clearing a Variant parameter in the client code.

See Also

COM Programming Introduction

Parameters

Return data


Return data

This topic discusses receiving parameters back from Interface member Methods and Property calls, translating the Variant data types to native PowerBASIC data types, and handling the return of Interfaces.

Parameters, assignment values, and return values must be passed as Variant variables.

Both the OBJECT GET statement (for Properties) and the OBJECT CALL statements (Methods) return values to the calling code. These return values must be received into a Variant variable. Therefore, before the data can be processed with normal BASIC statements, the Variant data must be converted to a suitable native data type.

PowerBASIC provides the VARIANT# function to convert numeric values held in a Variant variable, and the VARIANT$ function to convert string data held in a Variant variable.

The VARIANTVT function is used to determine the type of data held in a Variant, but any comparisons should use equates rather than hard-coded values. See VARIANTVT for more information.

Interface members can also return Interface references that can be used to access other COM Interfaces. These return values should be directly assigned to an object variable before the Interface they reference can be accessed. This is best shown by example, so we'll briefly reexamine a portion of the Microsoft Word™ code we presented a little earlier:

FUNCTION PBMAIN()

DIM oWordApp AS WordApplication ' Application Interface

DIM oWordDoc AS WordDocument ' Document Interface

DIM vVnt AS VARIANT

DIM sResult AS STRING

...

LET vVnt = 0

OBJECT CALL oWordApp.Documents.Add TO vVnt

LET oWordDoc = vVnt

LET vVnt = 0

OBJECT GET oWordApp.Documents.Count TO vVnt

sResult = "MSWORD has " + FORMAT$(VARIANT#(vVnt)) + _

" Files loaded!"

...

The relevant portions are underlined. The Method oWordApp.Documents.Add returns an Interface reference (of the type WordDocument), which the code receives back in the Variant vVnt. This is then assigned to the object variable oWordDoc and this Interface reference is used to access the oWordApp.Documents.Count Property of the WordDocument Interface.

When an Interface member returns an Interface reference, this is sometimes referred to as interface nesting.

See Also

COM Programming Introduction

What is a COM component?

What is a Dispatch Interface?

Methods and Properties

Enumerating collections

COM Programming Summary


Enumerating collections

A collection is simply a set or group of items, where each can be accessed through its own Interface. For example, Microsoft Word™ can have multiple documents open at the same time, and it can provide an Interface reference for each open document.

Therefore, enumerating a collection is simply a matter of determining the number of items in the collection, looping through and retrieving the appropriate information for one or more Interface members of the collection.

We'll start off with the Visual Basic syntax and show how to perform the same kind of task with PowerBASIC.

Visual Basic syntax for enumerating a collection looks something like this:

Dim Item As InterfaceItem

Dim Items As InterfaceItemsCollection

...

For Each Item In Items

'do something with the Item.member Method/Property, e.g.,

var$ = Item.StringProp

Next

In PowerBASIC, we can perform the same enumeration with just a little more code to handle the Variant data types. For example:

DIM oItem AS InterfaceItem

DIM oItems AS InterfaceItems

DIM vVnt AS VARIANT

...

OBJECT GET oItems.Count TO vVnt

FOR Index& = 1 TO VARIANT#(vVnt)

OBJECT GET oItems.Item(Index&) TO vVnt

LET oItem = vVnt

'do something with the Item.member Method/Property, e.g.,

OBJECT GET oItem.StringProp TO vVnt

var$ = VARIANT$(vVnt)

NEXT

See Also

COM Programming Introduction

The PowerBASIC COM Browser

Converting COM code to PowerBASIC

Example COM controller application

COM Programming Summary


Converting COM code to PowerBASIC

The default Interface member trap

When porting Visual Basic code to PowerBASIC (or other languages such as Delphi), it is sometimes necessary to explicitly add a member name to the ported code in PowerBASIC. This is required because Visual Basic uniquely supports a "short-hand" default Interface member that allows the name of the default Method to be omitted from the VB code.

For example, the following portions of VB code are equivalent:

var = Application.Documents(1)

var = Application.Documents.Item(1)

In the above code, the .Item() member is the default Property of the Documents Interface, and the first line of code takes advantage of VB's default member feature, allowing the name of the default Property to be omitted from the statement.

Usually, omitting a member name in code translated to PowerBASIC will not work correctly - it could produce a compile-time error or a run-time failure in the OBJECT statement. The type of error will depend on the exact definition of the Interface(s) being used.

In such cases, carefully study the COM component's documentation to establish if a named Interface member is missing from the OBJECT statement. In the case of Visual Basic, it is usually the .Item Property that is omitted.

Delphi does not support default Interface members, so Delphi COM code is less likely to cause these particular kinds of code conversion problems.

VB Method call syntax

In PowerBASIC, all Method and Property calls to Interface members must be performed with the OBJECT statement. However, Visual Basic permits a slightly less wordy syntax. For example:

Set Var = GetObject("progid")

Var.execute

The equivalent PowerBASIC syntax would look something like this:

LET Var = DISPATCH IN "progid"

OBJECT CALL Var.execute

See Also

COM Programming Introduction

The PowerBASIC COM Browser


Example COM controller application

The following code is a complete COM automation controller that exports a data table to Microsoft Excel™ and then displays a pie chart of the data. The code is heavily commented, and does not use a single API call, but does rely on the #INCLUDE file that contains the Interface definitions and numeric equates for Excel. This file was generated by the PowerBASIC COM Browser utility, and used without any editing.

This code can be used as the basis of a COM automation controller for your own applications.

Requires Excel 97 or later to be installed.

#COMPILE EXE

#INCLUDE "ExcelApp.inc"

FUNCTION PBMAIN

' Object Variables

DIM oExcelApp AS ExcelApplication

DIM oExcelWorkbook AS ExcelWorkbook

DIM oExcelWorkSheet AS ExcelWorkSheet

DIM oExcelChart AS ExcelChart

' General Object param variables

DIM vBool AS VARIANT

DIM vVnt AS VARIANT

DIM oVnt AS VARIANT

DIM vText AS VARIANT

DIM vRange AS VARIANT

DIM vX AS VARIANT

DIM vY AS VARIANT

' Chart Object param variables

DIM vGallery AS VARIANT

DIM vFormat AS VARIANT

DIM vPlotBy AS VARIANT

DIM vCatLabels AS VARIANT

DIM vSerLabels AS VARIANT

DIM vHasLegend AS VARIANT

DIM vTitle AS VARIANT

' SaveAs Object param variables

DIM vFile AS VARIANT

DIM vFileFmt AS VARIANT

' Open an instance of EXCEL

LET oExcelApp = ExcelApplication IN _

$PROGID_ExcelApplication8

IF ISFALSE ISOBJECT(oExcelApp) THEN _

LET oExcelApp = NEW ExcelApplication IN $PROGID_ExcelApplication8

' Could EXCEL be opened? If not, terminate this app

IF ISFALSE ISOBJECT(oExcelApp) THEN EXIT FUNCTION

' Create a new workbook in EXCEL

OBJECT CALL oExcelApp.WorkBooks.Add TO oVnt

' Copy the Interface reference into an Object Variable

LET oExcelWorkbook = oVnt

' Create a new worksheet in the workbook and use this

' worksheet reference to pump data into EXCEL

OBJECT CALL oExcelWorkBook.WorkSheets.Add TO oVnt

' Copy the Interface reference into an Object Variable

LET oExcelWorkSheet = oVnt

' Format and send data for cells A1:L1

LET vRange = "A1:G1"

' Create a Day of the Week array for A1:G1

DIM a$(1 TO 7)

a$(1) = "Monday" : a$(2) = "Tuesday"

a$(3) = "Wednesday" : a$(4) = "Thursday"

a$(5) = "Friday" : a$(6) = "Saturday"

a$(7) = "Sunday"

LET vText = a$()

OBJECT LET oExcelWorkSheet.Range(vRange).Value = vText

' Center the column title text

LET vVnt = %xlHAlignCenter

OBJECT LET oExcelWorkSheet.Range(vRange).HorizontalAlignment = vVnt

' Set the font attributes and border for this header line

OBJECT LET oExcelWorkSheet.Range(vRange).Font.Bold = vBool

' Color the text Blue

LET vVnt = RGB(0,0,255)

OBJECT LET oExcelWorkSheet.Range(vRange).Font.Color = vVnt

' Set the column width to approx 11 characters

LET vVnt = 11

OBJECT LET oExcelWorkSheet.Range(vRange).ColumnWidth = vVnt

' Enclose the table with a border

LET vRange = "A1:G2"

LET vVnt = %xlMedium ' Medium density border

OBJECT CALL oExcelWorkSheet.Range(vRange).BorderAround(Weight = vVnt)

' Format and send data for cells A2:G2

LET vRange = "A2:G2"

' Create a sales value array for each day of the week

DIM b@(1 TO 7)

RANDOMIZE TIMER

FOR y& = 1 TO 7

b@(y&) = RND(1,100000)

NEXT y&

LET vVnt = b@()

OBJECT LET oExcelWorkSheet.Range(vRange).Value = vVnt

' Format these cells in money (currency) format, ie: "$#.##"

LET vText = "Currency"

OBJECT LET oExcelWorkSheet.Range(vRange).Style = vText

' Now we can make EXCEL visible to the user

LET vBool = 1

OBJECT LET oExcelApp.Visible = vBool

' Prepare the chart

OBJECT GET oExcelApp.Charts.Add TO oVnt

' Copy the Interface reference into an Object Variable

LET oExcelChart = oVnt

' Set the range of cells to use in the table. Here it's 7x2

LET vRange = "A1:G2"

OBJECT GET oExcelWorkSheet.Range(vRange) TO vRange

' Set the Chart parameters

LET vGallery = %xl3DPie

LET vFormat = 7

LET vPlotBy = %xlRows

LET vCatLabels = 1

LET vSerLabels = 1

LET vHasLegend = 2

LET vTitle = "Sales percentages"

' Launch the Chart using named parameters

OBJECT CALL oExcelChart.ChartWizard( _

Source = vRange, _

Gallery = vGallery, _

Format = vFormat, _

PlotBy = vPlotBy, _

CategoryLabels = vCatLabels, _

SeriesLabels = vSerLabels, _

HasLegend = vHasLegend, _

Title = vTitle)

#IF %DEF(%PB_CC32)

PRINT "Press a key to continue the demo"

WAITKEY$

#ELSE

MSGBOX "Click to continue the demo", &H00002000& ' = %MB_TASKMODAL

#ENDIF

' un-Rem the next line for a Print Preview of the table

' Object call oExcelWorksheet.PrintPreview

' un-REM next line to stop "save workbook" prompting

' OBJECT LET oExcelWorkBook.Saved = vBool

' Save the Worksheet to disk - may trigger an "Overwrite?"

' prompt if the file already exists. We could either

' delete the file ahead of time or supply a unique name

LET vFile = "Test.Xls"

OBJECT CALL oExcelWorkSheet.SaveAs(vFile)

' Close the current Worksheet and close EXCEL

OBJECT CALL oExcelApp.ActiveWindow.Close

OBJECT CALL oExcelApp.Quit

' Release the Interfaces. We could omit this since the

' app is about to close, but "best practice" states we

' should clean our house before moving out.

LET oExcelApp = NOTHING

LET oExcelWorkbook = NOTHING

LET oExcelWorkSheet = NOTHING

LET oExcelChart = NOTHING

END FUNCTION

See Also

The PowerBASIC COM Browser

COM Programming Introduction


COM Programming Summary

Summary

A useable COM Object comprises a Dispatch Interface whose member Methods and Properties can be accessed by a COM client. An Object's Dispatch Interface is uniquely identifiable by its PROGID and CLSID. COM initialization must be considered carefully when creating multi-threaded applications and DLLs that use COM in secondary threads.

Bibliography

For more reading on COM theory and programming, the following publications may be of some assistance.

  • VB COM by Thomas Lewis, ISBN 1-861002-13-0

  • Windows 2000 Systems Programming by Al Williams, ISBN 1-57610-280-7

See Also

COM Programming Introduction


Upgrading To PBCC

Upgrading From DOS

This section compares the PowerBASIC Console Compiler for Windows to other BASIC products, including PowerBASIC for DOS (PB/DOS), Microsoft QuickBasic (MSBASIC), and Visual Basic for Windows (VB). The comparison is useful if you need to move source code to PB/CC in order to take advantage of its speed, power, and other features.

See Also

Language Incompatibilities

Default Variable Typing

Defining Constants

Graphics

Labels

Memory

Using Sequential Files

Using Binary Files

Using Random Access Files

Variable Types

Keyword Differences


Language Incompatibilities

The BASIC syntax used in PB/CC is based on the syntax of the award-winning PowerBASIC for DOS compiler. However, because of the different nature of Microsoft Windows, syntax modifications were necessary.

If you encounter a syntax incompatibility with your source code, please refer to the online help for the function or statement before you contact PowerBASIC Support.

See Also

Upgrading To PBCC

Creating Programs


Upgrading PBMAIN

Creating Programs

Unlike most DOS versions of BASIC, PB/CC requires that all executable code be contained inside a Sub or Function. Only scanned statements (such as metastatements, constant declarations, and declarations) can be placed outside of Subs and Functions. Therefore, every PB/CC program must have either a PBMAIN or a WINMAIN function so Windows can identify where to start executing your program. PBMAIN and WINMAIN are special functions reserved for exactly this purpose.

The following PB/DOS program:

PRINT "At the tone the time will be "; TIME$

BEEP

simply needs to be placed inside a Function called PBMAIN in order to execute:

FUNCTION PBMAIN

PRINT "At the tone the time will be "; TIME$

BEEP

END FUNCTION

When your program is loaded, the function PBMAIN (or WINMAIN) is executed automatically. This is where the main code of your application should be placed. The program stops when there are no more statements in PBMAIN, or an EXIT FUNCTION is encountered within PBMAIN.

See Also

Upgrading To PBCC

Default Variable Typing

What is a Console?

Hello, World! Example

Line numbers and Labels


Graphics Upgrading

PB/CC creates text mode console applications. You cannot draw pixels, circles, lines, etc, on the console screen. However, PB/CC does support the creation of one or more Graphic Windows with dazzling results. These are separate from the console screen and may display all forms of graphical images, including bitmaps, charts -- almost anything you can imagine.

See Also

Upgrading To PBCC

Labels

Graphics

Graphic Commands


Labels

In a PB/CC program, labels cannot have the same name as a function or a procedure. If Fred is a function name, it is illegal to have a label called Fred in your program. It is possible to have a variable and a label with the same name, however. In PB/DOS, you could not use the same label name more than once within the same scope of source code. In PB/CC, you can use the same name in more than one Sub or Function. Label names are local to Subs and Functions.

See Also

Upgrading To PBCC

Memory

Line numbers and Labels


Memory

The DOS operating system is not a protected mode operating system, and therefore is limited to 1 Megabyte of total memory (not including extensions like EMS or XMS). Because the CPU runs in what is called "Real Mode", it can only access memory in 64 Kb chunks (DOS is a 16-bit operating system, and 65,535 is the largest value that can be stored in a 16-bit Word) called segments.

The 32-bit Windows operating system, on the other hand, runs in protected mode, which allows it to access memory in 32-bit chunks. The largest value that can be stored in 32-bits is 4,294,967,295, or 4 Gigabytes. The operating system itself reserves half of that for itself, which leaves two Gigabytes for applications such as yours to store code, arrays, , etc.

Because Windows 95/98/ME and NT/2000/XP are all 32-bit operating systems, a 32-bit CPU register can store an entire 32-bit address, so segments are no longer needed or used. Therefore, functions like CODESEG, STRSEG, and VARSEG are no longer needed. CODEPTR, STRPTR, and VARPTR all return a complete 32-bit address. Additionally, when writing assembler code, you should never EVER change the values of the "segment" registers (CS, ES, DS, and SS). Otherwise, you can virtually guarantee an application crash or General Protection Fault (GPF).

See Also

Upgrading To PBCC

Using Sequential Files

The Inline Assembler

Flat memory model

Protected mode programming


Using Sequential Files

When PB/CC opens a file for APPEND, it does not adjust the file pointer to overwrite any existing CHR$(26) character (end of file mark, $EOF). Instead, it places new data at the "true" end of the file. PB/DOS overwrites the CHR$(26) marker.

See Also

Upgrading To PBCC

Using Binary Files

Using Random Access Files

Files

Random Access Files

Binary Files


Using Binary Files

PB/CC binary files start with byte 1, by default. PB/DOS binary files start with 0. To force compatibility, use the "BASE =" option in the OPEN statement.

OPEN "MYFILE.DAT" FOR BINARY AS #1 BASE = 0

See Also

Files

Sequential Files

Random Access Files

Binary Files


Using Random Access Files

PowerBASIC and MSBASIC both allow you to use the FIELD statement to define the fields for each record in a random access file. While previous versions of PB/CC did not support the FIELD statement, it is now supported with the FIELD statement. However, using fixed-length data types (such as a User-Defined Type, Union, fixed-length string or ASCIIZ string) with the GET and PUT statements is often more efficient.

Original Code:

OPEN "MYFILE.DAT" FOR RANDOM AS #1 LEN = 50

FIELD #1, 5 AS ID$

FIELD #1, 15 AS FIRSTNAME$

FIELD #1, 20 AS LASTNAME$

FIELD #1, 10 AS PHONE$

GET #1, 1 'get the first record

PRINT ID$

PRINT FIRSTNAME$

PRINT LASTNAME$

PRINT PHONE$

CLOSE #1

The field items need only be converted into a User-Defined Type, which can be used with the GET statement in PB/CC:

PB/CC Code:

TYPE Record

ID AS STRING * 5

FIRSTNAME AS STRING * 15

LASTNAME AS STRING * 20

PHONE AS STRING * 10

END TYPE

DIM r AS Record

OPEN "MYFILE.DAT" FOR RANDOM AS #1 LEN = 50

GET #1, 1, r 'get the first record

PRINT r.ID

PRINT r.FIRSTNAME

PRINT r.LASTNAME

PRINT r.PHONE

CLOSE #1

If you write a variable length string to a random access file with the PRINT statement in MSBASIC, you'll write more than the string. MSBASIC writes the length of the string as a two-byte Integer, which causes compatibility problems with existing code. PB/CC writes only the string and prints it exactly as it would to the console.

See Also

Files

Sequential Files

Random Access Files

Binary Files


Variable Types

PB/DOS introduced floating-point and fixed-point BCD variables - variable types specially suited to storage and calculation of monetary values. In Visual Basic, Microsoft introduced the Currency variable type, which is similar to a BCD variable, but with a fixed precision of four decimal places. PB/CC offers two Currency variable types: standard Currency (CUR) with four decimal places, and Extended-currency (CUX) optimized with two decimal places - both are based on the Quad-integer variable type. The CUR variable type is compatible with the VB Currency type.

PB/DOS 2.x introduced Flex strings, which were used for dynamic data structures. In place of Flex strings, PB/CC offers Field strings, which work in a similar fashion.

See Also

Upgrading To PBCC

Keyword Differences

Variables


Keyword Differences

Metastatements

PB/DOS and previous versions of PB/CC used the Dollar sign ($) to indicate a metastatement. This has been changed to a Number symbol (#). For example, $INCLUDE should be changed to #INCLUDE. The current version of PB/CC will still support the use of a leading Dollar sign for supported metastatements, but this feature will be eliminated in future versions.

$ALIAS/$CODE/$SEGMENT

Because Windows programs no longer have 64 Kb segment limitations, the $ALIAS, $CODE and $SEGMENT metastatements are no longer necessary. The $SEGMENT metastatement is supported, but ignored.

$COM/OPEN COM

The $COM metastatement and OPEN COM statement are no longer supported. All serial communications is done through the statement.

$CPU

PB/CC always compiles to native 32-bit flat mode machine code, so the $CPU metastatement is not supported.

$DYNAMIC

All arrays in PB/CC are created dynamically, so $DYNAMIC is no longer necessary.

$LINK Units and Libraries

The $LINK metastatement is no longer supported. You cannot link .OBJ files, units (.PBU) or static libraries (.PBL). You can use the DECLARE statement for linking to Dynamic Link Libraries (DLLs). The PowerBASIC GUI Compiler for Windows (PB/Win) can be used to compile common code into .DLLs, which can then be linked to your application.

ASCII

The PB/DOS ASCII function is replaced by the ASC function.

ATTRIB

For compatibility with Visual Basic, The ATTRIB statement was changed to SETATTR. The ATTRIB function was changed to GETATTR.

BLOAD/BSAVE

BLOAD and BSAVE are not supported. To load data to or from a file and a specified memory address, use GET$/PUT$ for the disk I/O and POKE$ and PEEK$ or variables to read/write the data to the memory location. As a benefit, these techniques can handle data sizes up to 2 Gb, rather than the 64 Kb limit of BLOAD and BSAVE.

CALL INTERRUPT/REG

The CALL INTERRUPT statement, REG statement, and REG functions are not supported. You cannot call real-mode DOS/BIOS interrupts from within a protected-mode Windows application. Most DOS and BIOS style interrupts calls have been replaced with API functions in Windows that 32-bit PowerBASIC applications should use instead.

CHAIN/COMMON

Because there are no longer any limitations to the size of applications you can create with PB/CC, the CHAIN and COMMON statements are not supported. If you require modular programs, you should use the SHELL statement, or create DLLs using the PowerBASIC GUI Compiler for Windows (PB/Win). If necessary, COMMON variables can be emulated with conventional disk files or Memory Mapped files (available through Windows API functions) to exchange data between programs.

CLEAR

In PB/DOS, the CLEAR statement was used to reset all variables, close all open files, and turn off event trapping. This is not supported in PB/CC. To close all open files you can use the CLOSE statement.

CODESEG/CODEPTR32/VARSEG/VARPTR32

The CODESEG and CODEPTR32 functions are not supported. The CODEPTR function returns a complete 32-bit address to any Sub, Function or Label in your code, (Labels are local to each Sub/Function in your program, so you cannot obtain a to a Label outside the scope of the current Sub or Function). 32-bit Windows uses a flat memory model, so segments are not used.

The VARSEG and VARPTR32 functions are not supported. The VARPTR function returns a complete 32-bit address to any variable or array element in your code.

COLOR

The COLOR statement does not provide the option to of change the CGA Border Color, which was a holdover from the days before EGA and VGA monitors. Console Windows do not support colored borders. However, the PB/CC COLOR statement offers new functionality to enable for setting the color attributes of multiple characters starting at the current caret position.

CRSLIN/POS

The CSRLIN function is replaced with the CURSORY function, and the POS function is replaced with the CURSORX function.

CVMS/CVMD/MKMS$/MKMD$

The CVMS, CVMD, MKMS$ and MKMD$ functions are not supported. If you need to access data stored in the obsolete Microsoft floating-point formats, source code is available on the PowerBASIC web site to mimic these functions. See Additional Source Code and Examples.

DATA/READ/RESTORE

DATA statements in PB/CC must be located inside a Sub or Function, and only data can be used. The READ statement is no longer used to read data, and the RESTORE statement is no longer valid. See the DATA, DATACOUNT, and READ$ statements for more information.

DEF SEG

The DEF SEG statement is no longer supported because of the flat-memory mode that protected mode Windows applications use. DOS applications often use the DEF SEG statement to access memory, such as the DOS DTA memory area to obtain file information. Such functionality can be obtained with Windows API functions instead. For example, the GetFileTime API can be used to obtain file date/time information.

DIM HUGE/DIM VIRTUAL

It is not necessary to use the HUGE or VIRTUAL keywords to create arrays larger than 64 Kb in size. PB/CC applications can access all available memory up to a maximum of two Gigabytes for arrays. No special support or keywords are needed.

END

The END statement is supported in a limited fashion for compatibility with DOS programming styles. However, it is much better to use the EXIT FUNCTION statement inside your PBMAIN or WINMAIN function to stop execution of your program prematurely.

To return an error level to the calling application, your program must have a PBMAIN or a WINMAIN function. The value assigned to PBMAIN or WINMAIN is then returned as the error level:

PBMAIN = 5 ' return an error level of 5 to PBMAIN()

or:

WINMAIN = 5 ' return an error level of 5 to WINMAIN()

or:

FUNCTION = 5 ' return the error level to either function

ERASE

In PB/DOS, using ERASE on a STATIC array simply clears the array back to its initialized state. In PB/CC, the RESET statement performs the same functionality. The PB/CC ERASE statement deallocates the memory regardless of whether the array is LOCAL, GLOBAL, or STATIC. This is similar to the way the PB/DOS ERASE statement works when used on dynamic arrays.

EXTERNAL/PUBLIC/SHARED

Windows does not allow different modules to share variables. Therefore the EXTERNAL, PUBLIC and SHARED keywords are no longer supported for setting variable scope. See the GLOBAL keyword for sharing variables between Subs and Functions.

INCLUDE

MSBASIC demands a colon after the $INCLUDE, and requires apostrophes around the file name. PB/CC uses no colon, and uses quotation marks around the file name.

INKEY$

The INKEY$ function and the PB/CC WAITKEY$ function are used to return keystroke codes; however, certain combinations such as CTRL+LeftArrow, etc, provide no indication in the keystroke code that the CTRL key modifier was used. To detect such keystrokes, use the INSHIFT function to determine whether the CTRL, ALT or SHIFT keys were pressed at the time the last INKEY$/WAITKEY$ key code was retrieved.

INP/OUT/WAIT

INP, OUT and WAIT are not supported. Windows NT security does not allow user-level applications to access hardware ports directly. In order to access a hardware port in Windows NT, you will need to use a kernel-mode device driver. Please visit http://www.lvr.com for more information.

Because Windows 95/98 and ME are hybrid operating systems, (unlike Windows NT/2000/XP which are pure 32-bit), they do not support the NT security model. This means you can directly access hardware ports, but it is not recommended. To do so, use the "!in" and "!out" assembler mnemonics.

INPUT/INPUT$

The INPUT statement is now supported for reading keyboard text. You may still use the LINE INPUT statement for reading keyboard text into a . If you need numeric input, you can use the VAL function to convert the string into a number.

The INPUT$ function is no longer supported for getting keyboard or file input. Use the INKEY$ function instead for reading keyboard input, or GET$ for reading from a file.

IOCTL/IOCTL$

The IOCTL statement and IOCTL$ function are no longer supported. They have been replaced by calls to the Windows API.

LOCATE

The LOCATE statement is no longer used to set the size and visibility of the caret. See the CURSOR statement.

LPOS

The LPOS function is no longer supported.

MTIMER

The MTIMER statement and function are no longer supported. See the API functions QueryPerformanceFrequency and QueryPerformanceCounter for information on high-resolution counters.

OPEN

The syntax for the OPEN statement no longer supports the following:

OPEN "modestring", filenum, filespec [, recordsize]

OPEN "COMx: "

In PB/DOS, the OPEN statement was used to access serial communications ports by directly accessing the hardware. PB/CC uses the COMM OPEN statement to begin access to serial ports in Windows.

OPTION [ARRAY] BASE

In PB/DOS, you can use OPTION BASE or OPTION ARRAY BASE statements to specify the first (LBOUND) dimension of an array. For example, by default:

DIM A(10)

…creates an array with 11 elements numbered 0 to 10. Using the OPTION BASE statement, you can change the default to 1:

OPTION BASE 1

DIM A(10)

…creates an array with 10 elements numbered from 1 to 10. In PB/CC, you cannot universally set the base subscript number for arrays. To dimension an array from 1 to 10, you must do so explicitly. For example:

DIM A(1 TO 10)

Note, however, that any array that does not begin with a subscript of zero will be less efficient when accessed. This is because the compiler must generate code to first subtract the base subscript number, before computing its location in memory.

OPTION BINARY BASE

In PB/DOS, the OPTION BINARY BASE statement is used to specify whether the lowest numbered byte position in a binary file is zero or one (the default is zero for PB/DOS, and one for MSBASIC).

PB/CC uses the default base of one, in keeping with Windows programming conventions. Furthermore, PB/CC does not support the OPTION statement, but instead provides a BASE clause in the OPEN statement, allowing the base byte position to be set for each individual disk file. Use BASE = 0 in the OPEN statement to mimic the default PB/DOS behavior.

PEEKI/PEEKL/POKEI/POKEL

The PEEKI and PEEKL functions are now supported by PEEK(INTEGER,x) and PEEK(LONG,x). Likewise, POKEI and POKEL statements are now supported by POKE INTEGER, addr, nexp and POKE LONG, addr, nexp.

PLAY/SOUND

The PLAY and SOUND statements are not supported. To output sound from your application, please see the Windows Multimedia API (for example, PlaySound, mciSendString, etc)

RESET

RESET in PB/DOS closes all open files. In PowerBASIC, RESET clears the contents of variables (including UDTs and Unions) and arrays. Use CLOSE with no parameters to close all open files, including COMM, TCP, and UDP files.

SHARED

Both PB/DOS and MSBASIC use the SHARED keyword to indicate variables that are shared among different Subs and Functions in your code. PowerBASIC uses the GLOBAL keyword to share variables between ALL Subs and Functions.

SYSTEM

The SYSTEM statement is no longer supported. To prematurely end your application, you must call the EXIT FUNCTION statement from within your PBMAIN or WINMAIN function, or jump to a label which is located before the END FUNCTION in PBMAIN or WINMAIN.

USING (PRINT USING/LPRINT USING)

PRINT USING and LPRINT USING have been superceded by the FORMAT$ and USING$ functions. FORMAT$ takes its parameters are in the reverse order to USING, and accepts only one argument, but offers a more flexible set of digit placeholders and format mask options. USING$ offers the same functionality as PRINT USING, but takes the form of a function rather than an output statement, permitting greater flexibility.

VIEW

View is not supported by PowerBASIC or Windows, although similar effects can often be achieved with judicious print positioning and the SCROLL statement.

See Also

Upgrading To PBCC


Appendix C - Optimizing your code

Optimizing your code

Internally, the DOS and 32-bit Windows operating systems are very different. DOS applications run in 16-bit "Real Mode", which means that the largest single data object is 64 Kilobytes (the largest 16-bit value is 65535). And because of the way "memory segmentation" works, the total addressing space available in "Real Mode" is a little over 1 Megabyte. Since the CPU is running in 16-bit mode (Real mode), numeric operations are fastest when variables are 16-bits (Integers and Words).

In contrast, 32-bit Windows runs in "Protected Mode", and the largest single data object is two Gigabytes (the largest 32-bit value is actually four Gigabytes, but the operating system reserves half of that for itself). Because the CPU is running in 32-bit mode (Protected mode), numeric operations are fastest when variables are 32-bits (Long-integers and Double-words).

Use 32-bit Variables

As you move your DOS code into PowerBASIC, you should replace all "Integer" and "Word" variable types with Long-integers and Double-words respectively - particularly in FOR/NEXT loops and integer-class math calculations. It actually takes the CPU longer to perform a calculation on a 2-byte Integer than it does with a 4-byte Long-integer, and it takes even longer with single byte variables.

Use Register Variables

Register variables are variables that are stored directly in specific CPU registers, rather than in application memory. Since data in a CPU register can be accessed much faster, and with less code, Register variables are valuable optimization tools.

Register variables are always local to the Sub or Function where they appear. In the current version of PowerBASIC, there may be up to two integer-class Register variables (Word/Dword/Integer/Long), and up to four Extended-precision floats. It is possible that future versions of the compiler will change these limits, so you may declare an unlimited number of them. Any "extra" Register variables are automatically reclassified as locals.

The REGISTER statement allows you to choose which variables will be classified as Register variables. If you do not make the choice in a particular Sub/Function, the compiler will attempt to choose for you. By default, the compiler will always assign the first two integer-class local variables available. Extended-precision float variables will be automatically assigned only in functions that contain no external function calls.

Integer class Register variables are most efficient for variables that are updated or used often, such as For/Next loop counter variables, and variables that are used repeatedly as array indexes. Floating-point Register variables should generally be chosen with a bit more caution, since the compiler must generate code to save and restore them to conventional memory around each call to a Sub or Function. In some rather rare cases, it is possible that floating-point Register variables could actually reduce execution speed. However, they are extremely valuable with intensive floating-point calculations and in functions that have few references to other Subs and Functions.

Due to the design of FPUs (floating point units), and the instruction sets available, the first float register variable declared in your program has far more optimization possibilities than the others do. Use care in choosing the variable which is used most within floating-point expressions (that is, on the right side of the '=' assignment operator), in order to gain the greatest advantage in execution speed. Also, remember it is typically valuable to assign floating-point numeric constants to Register variables when they are used in repetitive or intensive calculations.

You must use care with Inline Assembler floating-point opcodes in functions that enable Register variables. Floating-point Register variables may occupy up to four of the FPU registers, so you must limit your use of the x87 registers to the remaining four. Further, floating-point Register variables may never be referenced by name from Inline Assembler code, as the compiler cannot always track the register locations with absolute certainty.

Register variables are preserved when a call to an external DLL or API function is made. Register variables are automatically thread-safe too.

Because Register variables are stored within the CPU, it is not possible to use VARPTR on a register variable. When passing a register variable to a Sub/Function BYREF, the compiler temporarily converts the register variable into a memory variable, and reloads the register variable upon return from the Sub/Function call. The overhead that this adds is insignificant.

See Also

The Inline Assembler


Keyword Quick Finder

%

%DEF operator

#

#BLOAT metastatement

#COMPILE metastatement

#COMPILER metastatement

#CONSOLE metastatement

#DEBUG ERROR metastatement

#DEBUG PRINT metastatement

#DIM metastatement

#IF metastatement

#INCLUDE metastatement

#OPTION metastatement

#REGISTER metastatement

#RESOURCE metastatement

#STACK metastatement

#TOOLS metastatement

A

ABS function

ACODE$ function

AND operator

ARRAY ASSIGN statement

ARRAY DELETE statement

ARRAY INSERT statement

ARRAY SCAN statement

ARRAY SORT statement

ARRAYATTR function

ASC function

ASC statement

ASM statement

ATN function

B

BEEP statement

BGR function

BIN$ function

BIT CALC statement

BIT function

BIT statement

BITS function

BITS functions

C

CALL statement

CALL DWORD statement

CALLSTK statement

CALLSTK$ function

CALLSTKCOUNT function

CBYT function

CCUR function

CCUX function

CDBL function

CDWD function

CEIL function

CEXT function

CHDIR statement

CHDRIVE statement

CHOOSE function

CHOOSE& function

CHOOSE$ function

CHR$ function

CINT function

CLNG function

CLS function

CLOSE statement

CLSID$ function

CODEPTR function

COLOR statement

COMM CLOSE statement

COMM function

COMM LINE statement

COMM OPEN statement

COMM PRINT statement

COMM RECV statement

COMM RESET statement

COMM SEND statement

COMM SET statement

COMMAND$ function

CONSHNDL function

CONSIN function

CONSNAME$ function

CONSOLE GET LOC statement

CONSOLE GET SCREEN statement

CONSOLE GET SIZE statement

CONSOLE GET VIEW statement

CONSOLE GET VIRTUAL statement

CONSOLE NAME statement

CONSOLE SCREEN statement

CONSOLE SET FOCUS statement

CONSOLE SET LOC statement

CONSOLE SET SCREEN statement

CONSOLE SET VIEW statement

CONSOLE SET VIRTUAL statement

CONSOUT function

COS function

CQUD function

CSET statement

CSET$ function

CSNG function

CURDIR$ function

CURSOR function

CURSOR statement

CURSORX function

CURSORY function

CVBYT function

CVCUR function

CVCUX function

CVD function

CVDWD function

CVE function

CVI function

CVL function

CVQ function

CVS function

CVWRD function

CWRD function

D

DATA statement

DATACOUNT function

DATE$ system variable

DECLARE statement

DECR statement

DEFBYT statement

DEFCUR statement

DEFCUX statement

DEFDBL statement

DEFDWD statement

DEFEXT statement

DEFINT statement

DEFLNG statement

DEFQUD statement

DEFSNG statement

DEFSTR statement

DEFWRD statement

DESKTOP GET CLIENT statement

DESKTOP GET LOC statement

DESKTOP GET SIZE statement

DIM statement

DIR$ function

DIR$ CLOSE statement

DISKFREE function

DISKSIZE function

DO/LOOP statements

E

END statement

ENVIRON statement

ENVIRON$ function

EOF function

EQV operator

ERASE statement

ERL system variable

ERR system variable

ERRCLEAR system variable

ERROR statement

ERROR$ function

EXIT statement

EXP function

EXP2 function

EXP10 function

EXTRACT$ function

F

FIELD statement

FILEATTR function

FILECOPY statement

FILENAME$ function

FILESCAN statement

FIX function

FLUSH statement

FOR/NEXT statements

FORMAT$ function

FRAC function

FREEFILE function

FUNCNAME$ function

FUNCTION/END FUNCTION statements

G

GET statement

GET$ statement

GETATTR function

GETSTDERR function

GETSTDIN function

GETSTDKBD function

GETSTDOUT function

GETSTDVID function

GLOBAL statement

GOSUB statement

GOSUB DWORD statement

GOTO statement

GOTO DWORD statement

GRAPHIC ARC statement

GRAPHIC ATTACH statement

GRAPHIC BITMAP END statement

GRAPHIC BITMAP LOAD statement

GRAPHIC BITMAP NEW statement

GRAPHIC BOX statement

GRAPHIC CHR SIZE statement

GRAPHIC CLEAR statement

GRAPHIC COLOR statement

GRAPHIC COPY statement

GRAPHIC ELLIPSE statement

GRAPHIC FONT statement

GRAPHIC GET BITS statement

GRAPHIC GET CLIENT statement

GRAPHIC GET DC statement

GRAPHIC GET LOC statement

GRAPHIC GET MIX statement

GRAPHIC GET PIXEL statement

GRAPHIC GET POS statement

GRAPHIC GET PPI statement

GRAPHIC LINE statement

GRAPHIC PAINT statement

GRAPHIC PIE statement

GRAPHIC POLYGON statement

GRAPHIC POLYLINE statement

GRAPHIC PRINT statement

GRAPHIC REDRAW statement

GRAPHIC RENDER statement

GRAPHIC SAVE statement

GRAPHIC SCALE statement

GRAPHIC SET BITS statement

GRAPHIC SET FOCUS statement

GRAPHIC SET LOC statement

GRAPHIC SET MIX statement

GRAPHIC SET PIXEL statement

GRAPHIC SET POS statement

GRAPHIC STRETCH statement

GRAPHIC STYLE statement

GRAPHIC TEXT SIZE statement

GRAPHIC WIDTH statement

GRAPHIC WINDOW statement

GRAPHIC WINDOW END statement

GUID$ function

GUIDTXT$ function

H

HEX$ function

HI function

HIBYT function

HIINT function

HIWRD function

HOST ADDR statement

HOST NAME statement

I

IF statement

IF/END IF block

IIF function

IIF& function

IIF$ function

IMP operator

INCR statement

INKEY$ function

INPUT statement

INPUT# statement

INPUT FLUSH statement

INSHIFT function

INSTAT function

INSTR function

INT function

INTERFACE/END INTERFACE block

ISFALSE operator

ISNOTHING function

ISOBJECT function

ISTRUE operator

ITERATE statement

J

JOIN$ function

K

KEY statement

KEY$ function

KILL statement

L

LBOUND function

LCASE$ function

LEFT$ function

LEN function

LET statement

LINE INPUT statement

LINE INPUT# statement

LO function

LOBYT function

LOC function

LOCAL statement

LOCATE statement

LOCK statement

LOF function

LOG function

LOG2 function

LOG10 function

LOINT function

LOWRD function

LPRINT ATTACH statement

LPRINT CLOSE statement

LPRINT FLUSH statement

LPRINT FORMFEED statement

LPRINT statement

LPRINT$ function

LSET statement

LSET$ function

LTRIM$ function

M

MACRO/END MACRO block

MAK function

MAKDWD function

MAKINT function

MAKLNG function

MAKPTR function

MAKWRD function

MAT statement

MAX function

MAX& function

MAX$ function

MCASE$ function

MID$ function

MID$ statement

MIN function

MIN& function

MIN$ function

MKBYT$ function

MKCUR$ function

MKCUX$ function

MKD$ function

MKDWD$ function

MKE$ function

MKI$ function

MKL$ function

MKQ$ function

MKS$ function

MKWRD$ function

MKDIR statement

MOD operator

MOUSE statement

MOUSEBUTTONS function

MOUSESTAT

MOUSEX function

MOUSEY function

N

NAME statement

NOT operator

NUL$ function

O

OBJACTIVE function

OBJECT statement

OBJPTR function

OBJRESULT function

OCT$ function

ON ERROR statement

ON GOSUB statement

ON GOTO statement

OPEN statement

OPTION EXPLICIT statement

OR operator

P

PAGE statement

PAGEACTIVE function

PAGEVISIBLE function

PARSE statement

PARSE$ function

PARSECOUNT function

PBMAIN function

PCOPY statement

PEEK function

PEEK$ function

POKE statement

POKE$ statement

PRINT statement

PRINT# statement

PRINTER$ function

PRINTERCOUNT function

PROFILE statement

PROGID$ function

PUT statement

PUT$ statement

R

RANDOMIZE statement

READ$ function

REDIM statement

REGEXPR statement

REGISTER statement

REGREPL statement

REM statement

REMAIN$ function

REMOVE$ function

REPEAT$ function

REPLACE statement

RESET statement

RESUME statement

RETAIN$ function

RETURN statement

RGB function

RIGHT$ function

RMDIR statement

RND function

ROTATE statement

ROUND function

RSET statement

RSET$ function

RTRIM$ function

S

SCREEN function

SCREENATTR function

SCREENX function

SCREENY function

SCROLL statement

SEEK function

SEEK statement

SELECT CASE/END SELECT block

SET statement

SETATTR statement

SETEOF statement

SGN function

SHELL function

SHELL statement

SHIFT statement

SIN function

SIZEOF function

SLEEP statement

SPACE$ function

SQR function

STATIC statement

STDEOF function

STDERR statement

STDIN LINE statement

STDOUT statement

STR$ function

STRDELETE$ function

STRING$ function

STRINSERT$ function

STRPTR function

STRREVERSE$ function

SUB/END SUB statements

SWAP statement

SWITCH function

SWITCH& function

SWITCH$ function

T

TAB$ function

TALLY function

TAN function

TCP ACCEPT statement

TCP CLOSE statement

TCP LINE INPUT statement

TCP NOTIFY statement

TCP OPEN statement

TCP PRINT statement

TCP RECV statement

TCP SEND statement

THREADED statement

THREAD CLOSE statement

THREAD CREATE statement

THREAD RESUME statement

THREAD STATUS statement

THREAD SUSPEND statement

THREADCOUNT function

THREADID function

TIME$ system variable

TIMER function

TRACE statement

TRIM$ function

TRY/END TRY block

TYPE/END TYPE block

TYPE SET statement

U

UBOUND function

UCASE$ function

UCODE$ function

UDP CLOSE statement

UDP NOTIFY statement

UDP OPEN statement

UDP RECV statement

UDP SEND statement

UNION/END UNION block

UNLOCK statement

USING$ function

V

VAL function

VARIANT# function

VARIANT$ function

VARIANTVT function

VARPTR function

VERIFY function

W

WAITKEY$ function

WAITSTAT statement

WHILE/WEND statements

WINMAIN function

WRITE# statement

X

XOR operator

XPRINT statement

XPRINT ARC statement

XPRINT ATTACH statement

XPRINT BOX statement

XPRINT CANCEL statement

XPRINT CHR SIZE statement

XPRINT CLOSE statement

XPRINT COLOR statement

XPRINT COPY statement

XPRINT ELLIPSE statement

XPRINT FONT statement

XPRINT FORMFEED statement

XPRINT GET CLIENT statement

XPRINT GET DC statement

XPRINT GET LINES statement

XPRINT GET MARGIN statement

XPRINT GET MIX statement

XPRINT GET ORIENTATION statement

XPRINT GET PIXEL statement

XPRINT GET POS statement

XPRINT GET PPI statement

XPRINT GET QUALITY statement

XPRINT GET SIZE statement

XPRINT LINE statement

XPRINT PIE statement

XPRINT POLYGON statement

XPRINT POLYLINE statement

XPRINT RENDER statement

XPRINT SCALE statement

XPRINT SET MIX statement

XPRINT SET ORIENTATION statement

XPRINT SET PIXEL statement

XPRINT SET POS statement

XPRINT SET QUALITY statement

XPRINT STRETCH statement

XPRINT STYLE statement

XPRINT TEXT SIZE statement

XPRINT WIDTH statement

XPRINT$ function


Keyword Reference

This section contains an alphabetical listing of all of the PowerBASIC keywords. Each entry goes into specific detail about each command, and is cross-references to other relevant commands. The Programming Reference topics in this help file describe theory and example usage of a selection of essential commands.

The commands can be classified into four primary categories, according to their syntactic class: functions, statements, system variables, and metastatements:

Functions

These are predefined PowerBASIC functions, as opposed to user-defined functions. Functions generally return either a or a value, and these can be used within a more complex expression. Most functions require the program pass one or more arguments to them; these arguments being numeric or string, or combinations thereof, depending on the function. For example:

T = COS(3.1!)

sResult = FORMAT$(T)

A$ = CHR$(123, "hello", 65, 66, 67, 65 TO 97)

Statements

Statements are building blocks that make up programs. They instruct the compiler to perform specific actions, such as opening a file, setting the date, sending data to a device, etc. Statements do not return a value, but often take one or more arguments. Each statement must appear on a line by itself; or be separated from other program elements with a delimiting colon (:) character. For example:

A& = A& + 10& : B$ = "PowerBASIC"

OPEN "A Long Filename.txt" FOR BINARY AS #1

Count& = 100

System variables

System variables allow a program to interact with the system (in this sense, "system" means the computer, the operating system, the internal run-time code, etc). System variables are predefined by PowerBASIC, and can be used to access and control certain information maintained by the system. For example:

A$ = DATE$

DATE$ = "03-03-2003"

ErrVal = ERRCLEAR

B$ = TIME$

TIME$ = "03:00"

Metastatements

Metastatements are instructions that control the action of the PowerBASIC compiler. Strictly speaking, metastatements are not part of the BASIC language because they do not operate at run-time (when the program is executing). Like compiler option-switches, metastatements can be used to determine how the compiler will operate during the compilation of program code (compile-time).

Metastatements are prefixed with a number (#) symbol to differentiate them from normal statements. Metastatements should not be placed inside remarks, and there may be only one metastatement per program line. Metastatements may take one or more arguments. For example:

#COMPILE EXE "The target filename.exe"

#OPTION VERSION4

#DIM ALL

#INCLUDE "WIN32API.INC"

Please note that previous versions of PowerBASIC used the dollar ($) symbol as a metastatement prefix, but the number symbol has superceded the dollar symbol. PowerBASIC currently still supports the early syntax, but it is recommended that such metastatements be changed to the new syntax as soon as possible.

See Also

Format and typefaces

Command Summary


Format and typefaces

Every PowerBASIC command is listed alphabetically, as a separate topic. Each entry contains a brief explanation of what the command does, a description of its syntax, clarifying remarks and restrictions, plus examples of use. The examples are designed to be indicative of syntax and usage only.

The syntax section of each entry describes the available options and format each command may use, as follows:

Italic

Indicates areas within commands that you need to fill in with application-specific information, such as variable names, procedure names, numeric or string values, etc. For example:

y = VAL(string_expression)

UPPERCASE

Indicates part of the command must be entered exactly as shown. For example:

OPTION EXPLICIT

Brackets [ ]

Indicates the information they enclose is optional. For example:

SEEK [#] filenum&, position&&

Braces { }

Indicates a choice of two or more options, one of which MUST be used. For example:

#DIM {ALL | NONE}

Ellipses …

Indicates that part of the command can be repeated as many times as required. For example:

MACRO macroname [(prm1, prm2, ... )] = replacementtext

See Also

Keyword Reference

Command Summary


Command Summary

As described ealier, there are four basic categories of commands in PowerBASIC. The following breaks the command list into 19 groups of related commands, which can assist with identifying the best command for the task at hand. Some commands may appear in more than one group.

Command List

Array Operations

COM Commands

Communication Control

Compiler Operations

Debugging and Error Control

File Commands

Flow Control

Graphic Commands

Input Commands

Memory Management

Metastatements

Numeric Operations

Operating System

Printing Commands

Screen Text Commands

String Operations

Thread Control

Time Commands

Misc Operations

See Also

Keyword Reference

Format and typefaces


COM functions

COM Commands

The following functions can be used to create and manage COM clients:

ACODE$

Translate a Unicode string into an ANSI string

CLSID$

Return a 16-byte (128-bit) GUID string containing a CLSID

GUID$

Return a 16-byte (128-bit) Globally Unique Identifier GUID

GUIDTXT$

Return a 38-byte human-readable GUID/UUID string

INTERFACE/
END INTERFACE

Declare a dispatch interface with Methods/Properties

ISNOTHING

Determine the current status of a given object variable

ISOBJECT

Determine the current status of a given object variable

LET

Assign a value to a variable or Variant

OBJACTIVE

Return True/False of the running state of a COM EXE object

OBJECT

Communicate with a COM object through the dispatch interface

OBJPTR

Return an object pointer of a specified object variable

OBJRESULT

Return the execution result of the most recent OBJECT statement

PROGID$

Return the alphanumeric PROGID string (text) of a given CLSID

RESET

Clear a Variant to empty (%VT_EMPTY)

SET

Assign a Dispatch Interface reference to an object variable

UCODE$

Translate an ANSI into a Unicode string

VARIANT#

Return the numeric value contained in a Variant variable

VARIANT$

Return the dynamic string value contained in a Variant variable

VARIANTVT

Determine the internal data type of the data stored in a Variant


Communication functions

Communications Control

The following functions can be used for external communications:

COMM | Retrieve the value or status of a communications parameter
COMM CLOSE | Close an open serial port
COMM LINE | Receive a CR/LF terminated "line" of data from a serial port
COMM OPEN | Open a serial port
COMM PRINT | Send a "line" of binary data through a serial port
COMM RECV | Receive binary data from a serial port
COMM RESET | Disable flow control for a given serial port
COMM SEND | Send a string of binary data through a serial port
COMM SET | Set communication options for a serial port
CONSIN | Determine if standard input has been redirected
CONSOUT | Determine if standard output has been redirected
EOF | Return end-of-file status of a file, serial or TCP/UDP transmission
FREEFILE | Return the next available PowerBASIC file number
GETSTDERR | Return the handle of the standard error device
GETSTDIN | Return the handle of the standard input device
GETSTDKBD | Return the handle of the standard keyboard device
GETSTDOUT | Return the handle of the standard output device
GETSTDVID | Return the handle of a nominated console video page
HOST ADDR | Translate a host name into a corresponding IP address
HOST NAME | Translate an IP address into a corresponding host name
OPEN | Prepare a file or device for reading or writing
TCP ACCEPT | Accept an incoming request for TCP communication
TCP CLOSE | Close a previously opened TCP/IP port
TCP LINE INPUT | Receive a line of text from a specified TCP/IP port
TCP NOTIFY | Designate which TCP/IP events generate notification messages
TCP OPEN | Enable an app to communicate with a TCP/IP server or client
TCP PRINT | Write a string to a nominated TCP/IP
TCP RECV | Receive data from a specified TCP/IP port
TCP SEND | Write a string to a nominated TCP/IP port
UDP CLOSE | Close a previously opened UDP socket
UDP NOTIFY | Designate which TCP/IP events generate notification messages
UDP OPEN | Create a socket to communicate with a UDP server or client
UDP RECV | Receive data from a previously opened UDP port
UDP SEND | Send a string of data through a previously opened UDP socket


Compiler functions

Compiler Operations

The following functions manipulate the compiler's operation:

%DEF

Determine if an equate has been previously defined

#BLOAT

Artificially inflate the disk image size of a compiled program

#COMPILE

Determine which type of file will be created by the compiler

#CONSOLE

Enable or disable auto-creation of a console window upon execution of the program.

#DIM

Specify if variables must be declared before use

#IF

Define sections of source code to be compiled or ignored

#REGISTER

Control automatic allocation of Register variables

#STACK

Set the maximum potential stack size

#TOOLS

Enable/disable integrated development tools in compiled code

DECLARE

Explicitly declare a Sub or Function

DEFBYT

Declare the default variable type to be Byte

DEFCUR

Declare the default variable type to be Currency

DEFCUX

Declare the default variable type to be Extended Currency

DEFDBL

Declare the default variable type to be Double-precision

DEFDWD

Declare the default variable type to be Double-word

DEFEXT

Declare the default variable type to be Extended-precision

DEFINT

Declare the default variable type to be Integer

DEFLNG

Declare the default variable type to be Long-integer

DEFQUD

Declare the default variable type to be Quad-integer

DEFSNG

Declare the default variable type to be Single-precision

DEFSTR

Declare the default variable type to be String

DEFWRD

Declare the default variable type to be Word

DIM

Declare and dimension arrays, scalar variables, and

ERASE

Deallocate array memory

GLOBAL

Declare global (shared) variables between Subs and Functions

LOCAL

Declare local variables in a Sub or Function

MACRO

Define a single or multi-line text substitution block

OPTION EXPLICIT

Force explicit declaration of all variables

PBMAIN

Define the initial entry-point Function for an application

PROFILE

Capture an execution time profile of the Subs and Functions

REDIM

Declare dynamic arrays, allocate, reallocate, deallocate memory

REGISTER

Define local Register variables within a Sub or Function

STATIC

Declare static variables inside of a Sub or Function

STRPTR

Return the address of the data held by a variable length string

VARPTR

Return the 32-bit address of a variable or handle

WINMAIN

Define the initial entry-point Function for an application


Debugging and Error control functions

Debugging and Error Control

The following functions can be used to trap and manage error conditions:

#DEBUG ERROR

Control generation of error checking code

#DEBUG PRINT

Display information in the IDE's Debug Window

#DIM

Specify if variables must be declared before use

#STACK

Set the maximum potential stack size

#TOOLS

Enable/disable integrated development tools in compiled code

CALLSTK

Capture a representation of the stack frames in the call stack

CALLSTK$

Retrieve the details of a specific stack frame

CALLSTKCOUNT

Retrieve the number of stack frames in the call stack

ERL

Return the line number of the most recent run-time error

ERR

Return the error code of the most recent run-time error

ERRCLEAR

Return and clear the error code of the most recent run-time error

ERROR

Cause a specific run-time error to be generated and set ERR

ERROR$

Return a containing the descriptive name of an error

FILENAME$

Return the file-system name of an open file

FUNCNAME$

Return the name of the current Sub/Function

ON ERROR

Specify an error handling routine; enable/disable trapping

OPTION EXPLICIT

Force explicit declaration of all variables

PROFILE

Capture an execution time profile of the Subs and Functions

RESUME

Continue execution after error handling with ON ERROR GOTO

TRACE

Capture the precise flow of execution in a module

TRY/END TRY

A structured method of trapping and responding to errors


File functions

File Commands

The following functions can be used to manipulate files, standard I/O and disk services:

CHDIR

Change the current (default) directory on a given drive

CHDRIVE

Change the current default drive

CLOSE

Conclude I/O (input/output) to/from a file or device

CONSIN

Determine if standard input has been redirected

CONSOUT

Determine if standard output has been redirected

CURDIR$

Return the current directory for a given drive

DIR$

Return a filename that matches the given mask

DIR$ CLOSE

Force the release the operating system FindNext handle

DISKFREE

Return the amount of available space of a disk, in bytes

DISKSIZE

Return the total amount of space on a disk, in bytes

EOF

Return end-of-file status of a file, serial or TCP/UDP transmission

FILEATTR

Return information about an open file

FILECOPY

Copy a file

FILENAME$

Return the file-system name of an open file

FILESCAN

Rapidly scan a INPUT or BINARY file to obtain string size info

FLUSH

Flush file buffers to disk to ensure the disk information is current

FREEFILE

Return the next available PowerBASIC file number

GET

Read a record from a random-access file

GET$

Read a string from a file opened in binary mode

GETATTR

Return the file-system attribute(s) of a disk file or directory

INPUT#

Load variables with data from a sequential file

KILL

Delete a disk file

LINE INPUT#

Read line(s) from a sequential file into a variable or array

LOC

Determine the current seek position in an open disk file

LOCK

Lock part or all of an open file for exclusive access

LOF

Return the length of an open disk file

MKDIR

Create a subdirectory/folder (like the DOS MKDIR command)

NAME

Rename a file or a directory (like the DOS REN command)

OPEN

Prepare a file or device for reading or writing

PRINT#

Write data to a device or sequential file

PR OFILE

Create a file containing the time profile of Subs and Functions

PUT

Write a record to a random-access file or variable to a binary file

PUT$

Write a string to a file opened in binary mode

RMDIR

Delete a disk directory (like the DOS RMDIR command)

SEEK

File location where the next I/O operation will take place

SEEK

Set the position in a file for the next input or output operation

SETATTR

Set the file system attribute(s) of a disk file or directory

SETEOF

Truncate/extend a file to its current file pointer position

STDERR

Output a string expression to the standard error device

STDIN LINE

Read one line of text from the standard input device

STDOUT

Output a string expression to the standard output device

UNLOCK

Remove exclusive-access locks placed on a file

WIDTH#

Assign an output-line width to a sequential file

WRITE#

Output data to a sequential file in a delimited format


Flow Control functions

Flow Control

The following functions can be used to manage program execution/flow:

%DEF

Determine if an equate has been previously defined

#IF

Define sections of source code to be compiled or ignored

#TOOLS

Enable/disable integrated development tools in compiled code

CALL

Invoke a procedure (Sub or Function)

CALL DWORD

Invoke a procedure (Sub or Function) indirectly

CALLSTK

Capture a representation of the stack frames in the call stack

CALLSTK$

Retrieve the details of a specific stack frame

CALLSTKCOUNT

Retrieve the number of stack frames in the call stack

CHOOSE

Return one of several values, based upon the value of an index

CODEPTR

Obtain a 32-bit address of a label, Sub or Function

DO/LOOP

Define a group of statements that are executed repetitively

END

Terminate program immediately

EXIT

Transfer program execution out of a block structure

FOR/NEXT

Define a loop of program statements controlled by a counter

FUNCNAME$

Return the name of the current Sub/Function

FUNCTION/
END FUNCTION

Define a Function block

GOSUB

Invoke a local subroutine

GOSUB DWORD

Invoke a local subroutine indirectly

GOTO

Transfer program execution to the statement identified by a label

GOTO DWORD

Transfer execution indirectly to a local label or line number

IF

Test a condition and execute one or more program statements

IF/END IF

Create a IF/THEN/ELSE block with multiple lines and conditions

IIF

Return one of two values based upon a True/False evaluation

ISFALSE

Return the logical falsity of a given expression

ISNOTHING

Determine the current status of a given object variable

ISOBJECT

Determine the current status of a given object variable

ISTRUE

Return the logical truth of a given expression

ITERATE

Start an immediate iteration of a structure

MACRO

Define a single or multi-line text substitution block

ON ERROR

Specify an error handling routine; enable/disable trapping

ON GOSUB

Call one of several subroutines based on a numeric expression

ON GOTO

Send program flow to one of several labels based on a value

PBMAIN

Define the initial entry-point Function for an application

PROFILE

Capture an execution time profile of the Subs and Functions

RETURN

Return from a (GOSUB) subroutine to its caller

SELECT CASE

Control program flow based on the value of an expression

SLEEP

Pause the current thread for a specified number of milliseconds

SUB/END SUB

Define a Sub (procedure) block

TRY/END TRY

A structured method of trapping and responding to errors

WHILE/WEND

Define a block of statements that are executed repeatedly

WINMAIN

Define the initial entry-point Function for an application


Graphic functions

Graphic Commands

The following functions can be used to display graphics:

BGR | Convert an RGB color value to BGR format
GRAPHIC ARC | Draw an arc in the selected graphic window
GARAPHIC ATTACH | Select the graphic target (window or bitmap) on which future drawing operations will take place
GRAPHIC BITMAP END | Close the selected graphic bitmap
GRAPHIC BITMAP LOAD | Create a memory bitmap and load an image into it
GRAPHIC BITMAP NEW | Create a new memory bitmap
GRAPHIC BOX | Draw a box with square or rounded corners in the selected graphic window
GRAPHIC CHR SIZE | Retrieve the character size for the current font in the selected graphic window
GRAPHIC CLEAR | Clear the entire selected graphic window, optionally using a specified color and fill style
GRAPHIC COLOR | Set the foreground color and optionally the background color for various graphic statements
GRAPHIC COPY | Copy a bitmap to the selected graphic target
GRAPHIC ELLIPSE | Draw an ellipse or a circle in the selected graphic window
GRAPHIC FONT | Select a font for the GRAPHIC PRINT statement
GRAPHIC GET BITS | Retrieve a copy of a bitmap, storing it as a device-independent bitmap in a dynamic string variable
GRAPHIC GET CLIENT | Retrieve the client size of the selected graphic target
GRAPHIC GET DC | Retrieve the handle of the DC (device context) for the selected graphic window
GRAPHIC GET LOC | Retrieve the location of the selected graphic window on the screen
GRAPHIC GET MIX | Retrieve the color mix mode for the selected graphic target
GRAPHIC GET PIXEL | Retrieve the color of the pixel at the specified point in the selected graphic window
GRAPHIC GET POS | Retrieve the POS (last point referenced) by a graphic statement
GRAPHIC GET PPI | Retrieve the resolution of the display device, in points per inch
GRAPHIC LINE | Draw a line in the selected graphic window
GRAPHIC PAINT | Fill an area with a solid color or a hatch pattern
GRAPHIC PIE | Draw a pie section on the selected graphic target
GRAPHIC POLYGON | Draw a polygon in the selected graphic target
GRAPHIC POLYLINE | Draw a series of connected line segments
GRAPHIC PRINT | Output text to the selected graphic target
GRAPHIC REDRAW | Update buffered graphical statements, drawing them to the selected graphic target
GRAPHIC RENDER | Render an image on the selected graphic target
GRAPHIC SAVE | Save an image to a bitmap (.BMP) file
GRAPHIC SCALE | Define a custom coordinate system for the graphic target
GRAPHIC SET BITS | Replace a copy of a bitmap that was retrieved as a device-independent bitmap
GRAPHIC SET FOCUS | Bring the selected graphic window to the foreground and direct focus to it
GRAPHIC SET LOC | Change the location of the selected graphic window on the screen
GRAPHIC SET MIX | Set the color mix mode for the selected graphic target
GRAPHIC SET PIXEL | Draw a single pixel to the selected graphic window
GRAPHIC SET POS | Set the last point referenced (POS) for the selected graphic target
GRAPHIC STRETCH | Copy and resize a bitmap to the selected graphic target
GRAPHIC STYLE | Set the line style to be used by various graphical statements in the selected graphic target
GRAPHIC TEXT SIZE | Calculate the size of text to be printed
GRAPHIC WIDTH | Set the line width to be used by various graphical statements in the selected graphic target
GRAPHIC WINDOW | Create a new graphic window
GRAPHIC WINDOW END | Close and destroy the selected graphic window
RGB | Return an RGB color value for use with the Windows API palette and GDI functions


Input functions

Input Commands

The following functions can be used to gather input data:

COMM

Retrieve the value or status of a communications parameter

COMM LINE

Receive a CR/LF terminated "line" of data from a serial port

COMM RECV

Receive binary data from a serial port

COMMAND$

Return the command-line used to start the program

ENVIRON

Modify the current program's environment table.

ENVIRON$

Retrieve strings from the operating system's environment table

EOF

Return end-of-file status of a file, serial or TCP/UDP transmission

FIELD

Bind a field string to a file buffer or dynamic string variable

FILESCAN

Rapidly scan a INPUT or BINARY file to obtain string size info

FREEFILE

Return the next available PowerBASIC file number

GET

Read a record from a random-access file

GET$

Read a from a file opened in binary mode

GETSTDIN

Return the handle of the standard input device

GETSTDKBD

Return the handle of the standard keyboard device

INKEY$

Read a keyboard or mouse event from the console input buffer

INPUT

Read a line from the keyboard, assigning data to one or more variables

INPUT#

Load variables with data from a sequential file

INPUT FLUSH

Empty all pending keyboard and mouse events from the buffer

INSHIFT

Return the state of the keyboard Shift, Ctrl and/or Alt keys

INSTAT

Determine whether a keyboard or mouse event is available

KEY

Assign a string expression to a function key (F1 to F12)

KEY$

Retrieve the string expression assigned to a function key with the KEY statement

LINE INPUT

Read an entire line from the keyboard into a string variable

LINE INPUT#

Read line(s) from a sequential file into a string variable or array

LOC

Determine the current seek position in an open disk file

LOF

Return the length of an open disk file

MOUSE

Turn on/off mouse event trapping and define required mouse events

MOUSEBUTTONS

Return the number of buttons on the system mouse

MOUSESTAT

Determine if a mouse event is waiting to be retrieved

MOUSEX

Return the current column (x coordinate) of the mouse cursor

MOUSEY

Return the current row (y coordinate) of the mouse cursor

PEEK

Return the byte at a specific memory location

PEEK$

Return a sequence of bytes starting at a specific memory location

STDEOF

Return TRUE if the end of standard input has been reached

STDIN LINE

Read one line of text from the standard input device

WAITKEY$

Read a character or mouse event from the console input buffer

WAITSTAT

Wait until a key is pressed or a mouse event occurs


Memory Management

The following functions manipulate memory and its allocation/deallocation:

#REGISTER

Control automatic allocation of Register variables

#STACK

Set the maximum potential stack size

CODEPTR

Obtain a 32-bit address of a label, Sub or Function

DIM

Declare and dimension arrays, scalar variables, and

ERASE

Deallocate array memory

GLOBAL

Declare global (shared) variables between Subs and Functions

LEN

Return the logical length of a variable, UDT, or Union

LET

Assign a Variant to an array or an array to a Variant

LOCAL

Declare local variables in a Sub or Function

PEEK

Return the byte at a specific memory location

PEEK$

Return a sequence of bytes starting at a specific memory location

POKE

Store a byte at a specific memory location

POKE$

Store a sequence of bytes starting at a specific memory location

REDIM

Declare dynamic arrays, allocate, reallocate, deallocate memory

REGISTER

Define local Register variables within a Sub or Function

RESET

Set an array subscript or an entire array to zero or null/empty

SIZEOF

Return the total or physical length of any PowerBASIC variable

STATIC

Declare static variables inside of a Sub or Function

STRPTR

Return the address of the data held by a variable length string

THREADED

Declare thread-local variables

TYPE/END TYPE

Define a User-Defined Type and one or more member elements

TYPE SET

Assign the value of a UDT or string expression to a UDT

UNION/END UNION

Create a UDT-like structure whose elements overlap in memory

VARPTR

Return the 32-bit address of a variable or string handle


Metastatements

The following functions control compiler and debugger behavior:

%DEF | Determine if an equate has been previously defined
#BLOAT | Artificially inflate the disk image size of a compiled program
#COMPILE | Determine which type of file will be created by the compiler
#COMPILER | Define the compiler for this program
#CONSOLE | Enable or disable auto-creation of a console window upon execution of the program.
#DEBUG ERROR | Control generation of error checking code
#DEBUG PRINT | Display information in the IDE's Debug Window
#DIM | Specify if variables must be declared before use
#IF | Define sections of source code to be compiled or ignored
#INCLUDE | Instruct the compiler to read an additional source file from disk
#OPTION | Mark the file as requiring NT3.5; Win95/NT4; or Win2000/XP
#REGISTER | Control automatic allocation of Register variables
#RESOURCE | Embed a PowerBASIC Resource file into the executable file
#STACK | Set the maximum potential stack size
#TOOLS | Enable/disable integrated development tools in compiled code


Numeric Operations

The following functions manipulate and manage data:

ABS

Return the absolute value of a numeric expression

AND

AND works as both a logical and a bitwise arithmetic operator

ARRAY ASSIGN

Assign a number of values to successive elements of an array

ARRAY DELETE

Delete a single item from a given array

ARRAY INSERT

Insert a single item into a given array

ARRAY SCAN

Scan all or part of an array for a given value

ARRAY SORT

Sort all or part of a given array

ASC

Return the ASCII code of the specified character in a

ASC

Place an ASCII byte at the specified position in a string

ATN

Return the arctangent of its argument

BIN$

Return a string with the binary (base 2) representation of a value

BIT CALC

Set or reset a bit in an variable

BIT

Return the value of a particular bit in an integer-class variable

BIT

Manipulate individual bits of an integer-class variable

BITS

Return the least significant portion of an integer class value

BITS

Return the least significant 8, 16, or 32 bits of an argument

CBYT

Convert a value to a Byte data type

CCUR

Convert a value to a Currency data type

CCUX

Convert a value to a Extended Currency data type

CDBL

Convert a value to a Double-precision data type

CDWD

Convert a value to a Double-word data type

CEIL

Return an that is greater than or equal to an argument

CEXT

Convert a value to a Extended-precision data type

CHOOSE

Return one of several values, based upon the value of an index

CINT

Convert a value to a Integer data type

CLNG

Convert a value to a Long-integer data type

COS

Return the cosine of an argument

CQUD

Convert a value to a Quad-integer data type

CSNG

Convert a value to a Single-precision data type

CVBYT

Convert binary encoded string data to a byte value

CVCUR

Convert binary encoded string data to a Currency value

CVCUX

Convert binary encoded string data to Extended Currency

CVD

Convert binary encoded string data to a Double-precision value

CVDWD

Convert binary encoded string data to a Double-word value

CVE

Convert binary encoded string data to Extended-precision

CVI

Convert binary encoded string data to an Integer value

CVL

Convert binary encoded string data to a Long-integer value

CVQ

Convert binary encoded string data to a Quad-integer value

CVS

Convert binary encoded string data to a Single-precision value

CVWRD

Convert binary encoded string data to a Word value

CWRD

Convert a value to a Word data type

DECR

Decrement a variable, , or pointer target

DEFBYT

Declare the default variable type to be Byte

DEFCUR

Declare the default variable type to be Currency

DEFCUX

Declare the default variable type to be Extended Currency

DEFDBL

Declare the default variable type to be Double-precision

DEFDWD

Declare the default variable type to be Double-word

DEFEXT

Declare the default variable type to be Extended-precision

DEFINT

Declare the default variable type to be Integer

DEFLNG

Declare the default variable type to be Long-integer

DEFQUD

Declare the default variable type to be Quad-integer

DEFSNG

Declare the default variable type to be Single-precision

DEFSTR

Declare the default variable type to be String

DEFWRD

Declare the default variable type to be Word

EQV

Perform a logical or a bitwise Equivalence operation

EXP

Return a base number raised to a power, with a base of e

EXP2

Return a base number raised to a power, with a base of 2

EXP10

Return a base number raised to a power, with a base of 10

FIX

Truncate a number to an integer

FORMAT$

Format numeric data according to a string mask expression

FRAC

Return the fractional part of a floating-point number

HEX$

Hexadecimal (base 16) string representation of an argument

HI

Extract the most significant (high-order) portion of an argument

HIBYT

Extract the most significant (high-order) Byte of an argument

HIINT

Extract the most significant (high-order) Integer of an argument

HIWRD

Extract the most significant (high-order) Word of an argument

IIF

Return one of two values based upon a True/False evaluation

IMP

Perform a logical or a bitwise Implication operation

INCR

Decrement a variable, pointer, or pointer target

INT

Convert a numeric expression to an integer-class value

ISFALSE

Return the logical falsity of a given expression

ISNOTHING

Determine the current status of a given object variable

ISOBJECT

Determine the current status of a given object variable

ISTRUE

Return the logical truth of a given expression

LBOUND

Return the lowest subscript of an array's specific dimension

LEN

Return the logical length of a variable, UDT, or Union

LET

Assign a numeric variable or numeric array to a Variant

LO

Extract the least significant (low-order) portion of an argument

LOBYT

Extract the least significant (low-order) Byte of an argument

LOG

Return the natural (base e) logarithm of an argument

LOG2

Return the base 2 logarithm of an argument

LOG10

Return the base 10 logarithm of an argument

LOINT

Extract the least significant (low-order) Integer of an argument

LOWRD

Extract the least significant (low-order) Word of an argument

MAKDWD

Combine two 16-bit arguments to form a Double-word

MAKINT

Combine two 8-bit arguments to form an Integer

MAKLNG

Combine two 16-bit arguments to form a Long-integer

MAKPTR

Combine two 16-bit arguments to form a 32-bit pointer

MAKWRD

Combine two 8-bit arguments to form a Word

MAT

Matrix calculations on numeric arrays

MAX

Return the argument with the largest (maximum) value

MIN

Return the argument with the smallest (minimum) value

MOD

Return the remainder of the division between two numbers

NOT

The NOT operator works as a bitwise arithmetic operator

OCT$

Return a string that is a octal (base 8) representation of a value

OR

Perform a logical or a bitwise OR arithmetic operation

PEEK

Return the byte at a specific memory location

POKE

Store a byte at a specific memory location

RANDOMIZE

Seed the random number generator

RESET

Set a variable, array subscript, or an entire array to zero

RGB

Return a composite RGB color value

RND

Return a random number

ROTATE

Rotate the bits in an integer-class variable

ROUND

Round a numeric value to a specified number of decimal places

SGN

Return the sign of a numeric expression

SHIFT

Shift the bits in an integer-class variable

SIN

Return the sine of an argument

SQR

Return the square root of an argument

SWAP

Exchange the values of two variables, pointers, or pointer targets

SWITCH

Return one item of a series based upon a True/False evaluation

TAN

Return the tangent of an argument

UBOUND

Return the highest subscript of an array's specific dimension

USING$

Format string/numeric expressions using a mask string

VAL

Return the numeric equivalent of a string argument

VARIANT#

Return the numeric value contained in a Variant variable

XOR

Perform a logical or a bitwise Exclusive-OR operation


Operating System

The following functions manipulate file and operating system features:

CHDIR

Change the current (default) directory on a given drive

CHDRIVE

Change the current default drive

COMMAND$

Return the command-line used to start the program

CONSHNDL

Return the window handle of the console window

CONSIN

Determine if standard input has been redirected

CONSNAME$

Return the name in the console window caption

CONSOLE GET LOC

Retrieve the location of the console window on the screen

CONSOLE GET SCREEN

Retrieve the size of the visible part of the console, in text rows and columns

CONSOLE GET SIZE

Retrieve the size of the console window, in pixels

CONSOLE GET VIEW

Retrieve the position of the viewable part of a virtual screen

CONSOLE GET VIRTUAL

Retrieve the size of the screen buffers associated with the console window

CONSOLE NAME

Set the name of the console window caption

CONSOLE SCREEN

Change the size of the console window

CONSOLE SET FOCUS

Bring the console window to the foreground and direct the focus to it

CONSOLE SET LOC

Move the console window to a new location on the screen

CONSOLE SET SCREEN

Change the size of the console window and its associated screen buffers

CONSOLE SET VIEW

Set the position of the viewable part of a virtual screen

CONSOLE SET VIRTUAL

Change the size of the buffers associated with the console window

CONSOUT

Determine if standard output has been redirected

CURDIR$

Return the current directory for a given drive

CURSOR

Return the size status of the caret

CURSOR

Set the caret size and visibility

DATE$

Set and retrieve the system date

DESKTOP GET CLIENT

Retrieve the size of the client area of the desktop, in pixels

DESKTOP GET LOC

Retrieve the location of the top, left corner of the client area of the desktop, in pixels

DESKTOP GET SIZE

Retrieve the size of the entire desktop, in pixels

DIR$

Return a filename that matches the given mask

DIR$ CLOSE

Force the release the operating system FindNext handle

DISKFREE

Return the amount of available space of a disk, in bytes

DISKSIZE

Return the total amount of space on a disk, in bytes

ENVIRON

Modify the current program's environment table

ENVIRON$

Retrieve from the operating system's environment table

FILEATTR

Return information about an open file

FILECOPY

Copy a file

FILENAME$

Return the file-system name of an open file

FLUSH

Flush file buffers to disk to ensure the disk information is current

GETATTR

Return the file-system attribute(s) of a disk file or directory

GETSTDERR

Return the handle of the standard error device

GETSTDIN

Return the handle of the standard input device

GETSTDKBD

Return the handle of the standard keyboard device

GETSTDOUT

Return the handle of the standard output device

GETSTDVID

Return the handle of a nominated console video page

HOST ADDR

Translate a host name into a corresponding IP address

HOST NAME

Translate an IP address into a corresponding host name

KILL

Delete a disk file

MKDIR

Create a subdirectory/folder (like the DOS MKDIR command)

MOUSE

Turn on/off mouse event trapping and define required mouse events

MOUSEBUTTONS

Return the number of buttons on the system mouse

MOUSESTAT

Determine if a mouse event is waiting to be retrieved

MOUSEX

Return the current column (x coordinate) of the mouse cursor

MOUSEY

Return the current row (y coordinate) of the mouse cursor

NAME

Rename a file or a directory (like the DOS REN command)

OPEN

Prepare a file or device for reading or writing

PAGE

Determine which of 8 console text pages is active and/or visible

PAGEACTIVE

Return the current active console page number

PAGEVISIBLE

Return the number of the visible console page

PCOPY

Copy all text from one console text page (screen) to another

RGB

Return a composite RGB color value

RMDIR

Delete a disk directory (like the DOS RMDIR command)

SETATTR

Set the file system attribute(s) of a disk file or directory

SETEOF

Truncate/extend a file to its current file pointer position

SHELL

Launch an executable program asynchronously

SHELL

Launch an executable program synchronously

SLEEP

Pause the current thread for a specified number of milliseconds

STDEOF

Return TRUE if the end of standard input has been reached

STDERR

Output a string expression to the standard error device

STDIN LINE

Read one line of text from the standard input device

STDOUT

Output a string expression to the standard output device


Printing Commands

The following functions are used to send data to a printer:

LPRINT | Output text and data to a printer device
LPRINT ATTACH | Connect directly to a line printer device
LPRINT CLOSE | Disconnect the current printer device
LPRINT FLUSH | Flush any remaining print data to the printer device
LPRINT FORMFEED | Send a formfeed (page eject) character to the printer
LPRINT$ | Return the current printer device used for LPRINT operations
PRINTER$ | Retrieve printer names and printer port names
PRINTERCOUNT | Retrieve the number of available (installed) printers
XPRINT | Output text to a host-printer device
XPRINT ARC | Draw an arc on a host printer page
XPRINT ATTACH | Connect a host-based (GDI) printer for use with XPRINT
XPRINT BOX | Draw a box with square or rounded corners on a host printer page
XPRINT CANCEL | Cancel a print job on the host printer
XPRINT CHR SIZE | Retrieve the character size for the current font on a host printer page
XPRINT CLOSE | Detach a host printer so printing may begin
XPRINT COLOR | Set the foreground color (and, optionally, the background color) for various XPRINT statements
XPRINT COPY | Copy a bitmap to a host printer page
XPRINT ELLIPSE | Draw an ellipse or a circle on a host printer page
XPRINT FONT | Select a font to be used by the XPRINT statement
XPRINT FORMFEED | Start a new page for the host printer
XPRINT GET CLIENT | Retrieve the size of the client area (printable area) on the host printer page
XPRINT GET DC | Retrieve the handle of the device context (DC) for the host printer page
XPRINT GET LINES | Retrieve the number of lines that can be printed
XPRINT GET MARGIN | Retrieve the margin sizes for the host printer
XPRINT GET MIX | Retrieve the color mix mode for a host printer page
XPRINT GET ORIENTATION | Retrieve the paper orientation for a host printer page
XPRINT GET PIXEL | Retrieve the color of a pixel on a host printer page
XPRINT GET POS | Retrieve the last point referenced (POS) by an XPRINT statement
XPRINT GET PPI | Retrieve the resolution of the host printer page
XPRINT GET QUALITY | Retrieve the print quality setting for the host printer
XPRINT GET SIZE | Retrieve the total size of the host printer page
XPRINT LINE | Draw a line on a host printer page
XPRINT PIE | Draw a pie section on a host printer page
XPRINT POLYGON | Draw a polygon on a host printer page
XPRINT POLYLINE | Draw a series of connected lines on a host printer page
XPRINT RENDER | Render an image on a host printer page
XPRINT SCALE | Define a custom world coordinate system for a host printer page
XPRINT SET MIX | Set the color mix mode for a host printer page
XPRINT SET ORIENTATION | Set the paper orientation for a host printer page
XPRINT SET PIXEL | Set the color of a pixel on a host printer page
XPRINT SET POS | Retrieve the last point referenced (POS) by an XPRINT statement
XPRINT SET QUALITY | Set the print quality for a host printer
XPRINT STRETCH | Copy and resize a bitmap to a host printer page
XPRINT STYLE | Set the line style to be used by various XPRINT statements
XPRINT TEXT WIDTH | Calculate the size of text to be printed on a host printer
XPRINT WIDTH | Set the graphic line width to be used by various XPRINT statements
XPRINT$ | Return the name of the attached host printer


Screen Text Commands

The following functions control the display of console text:

? | Write data to the current console text page (screen)
CLS | Clear the active console screen page
COLOR | Set the foreground and background colors for displaying text
CONSNAME$ | Return the name in the console window caption
CONSOLE GET LOC | Retrieve the location of the console window on the screen
CONSOLE GET SCREEN | Retrieve the size of the visible part of the console, in text rows and columns
CONSOLE GET SIZE | Retrieve the size of the console window, in pixels
CONSOLE GET VIEW | Retrieve the position of the viewable part of a virtual screen
CONSOLE GET VIRTUAL | Retrieve the size of the screen buffers associated with the console window
CONSOLE NAME | Set the name of the console window caption
CONSOLE SCREEN | Change the size of the console window
CONSOLE SET FOCUS | Bring the console window to the foreground and direct the focus to it
CONSOLE SET LOC | Move the console window to a new location on the screen
CONSOLE SET SCREEN | Change the size of the console window and its associated screen buffers
CONSOLE SET VIEW | Set the position of the viewable part of a virtual screen
CONSOLE SET VIRTUAL | Change the size of the buffers associated with the console window
CONSOUT | Determine if standard output has been redirected
CURSOR | Return the size status of the caret
CURSOR | Set the caret size and visibility
CURSORX | Return the current column of the caret
CURSORY | Return the current row of the caret
DESKTOP GET CLIENT | Retrieve the size of the client area of the desktop, in pixels
DESKTOP GET LOC | Retrieve the location of the top, left corner of the client area of the desktop, in pixels
DESKTOP GET SIZE | Retrieve the size of the entire desktop, in pixels
GETSTDERR | Return the handle of the standard error device
GETSTDOUT | Return the handle of the standard output device
GETSTDVID | Return the handle of a nominated console video page
INPUT | Read a line from the keyboard, assigning data to one or more variables
LOCATE | Set the caret position
PAGE | Determine which of 8 console text pages is active and/or visible
PAGEACTIVE | Return the current active console page number
PAGEVISIBLE | Return the current visible console page number
PCOPY | Copy all text from one console text page (screen) to another
PRINT | Write data to the current console text page (screen)
SCREEN | Return the ASCII code of a character on the current console page
SCREENATTR | Return a composite character attribute (color)
SCREENX | Return the number of columns (x coordinate) on the console screen
SCREENY | Return the number of rows (y coordinate) on the console screen
SCROLL | Scroll all or part of the current console page (screen)
STDERR | Output a string expression to the standard error device
STDOUT | Output a string expression to the standard output device


Thread Control

The following functions are used to create and manage threads:

THREADED | Declare Thread Local Storage (TLS) variables
THREAD CLOSE | Close a Windows thread
THREAD CREATE | Create a Windows thread
THREAD RESUME | Resume execution of a suspended Windows thread
THREAD STATUS | Retrieve the Status of a Windows thread
THREAD SUSPEND | Suspend execution of a Windows thread
THREADCOUNT | Return the number of active threads that exist in a module
THREADID | Return a Long-integer thread identifier of the current thread


Time Commands

The following functions manipulate and manage time and the system date:

DATE$ | Set and retrieve the system date
SLEEP | Pause the current thread for a specified number of milliseconds
TIME$ | Read and/or set the system time
TIMER | Return the number of seconds that have elapsed since midnight


Misc. Operations

Miscellaneous functions:

ASM | Identify an assembly-language statement
BEEP | Play the default Windows sound through the computer speaker(s)
REM | Indicates the remainder of a line of source code is a remark or comment


%DEF operator

Purpose

Determine if an has been previously defined.

Syntax

%DEF({%numeric_equate | $string_equate})

Remarks

The %DEF operator tests whether or not an equate has been defined. If the equate has been defined, %DEF returns TRUE (non-zero); or FALSE (zero) if it has not be defined.

PowerBASIC automatically defines the equates in the following table according to the PowerBASIC compiler being used. Please note the references to other PowerBASIC compilers are included for those writing programs that may be compilable by more than one PowerBASIC compiler.

Equate

Definition

%PB_CC32

Pre-defined as TRUE (non-zero) in PB/CC for Windows, but is not defined in other compilers.

%PB_DLL16

Pre-defined as TRUE (non-zero) in PB/DLL 16-bit, but is not defined in other compilers.

%PB_DLL32

Synonym of %PB_WIN32

%PB_WIN32

Pre-defined as TRUE (non-zero) in PB/Win 32-bit, but is not defined in other compilers.

%PB_REVISION

Pre-defined as the hex revision (8.00 = &H800).

%PB_REVLETTER

Pre-defined as the ASCII code of the revision letter (a = &H61), or &H20 if there is no revision letter.

%PB_EXE

Pre-defined as TRUE (non-zero) if compiling to EXE or CON format (CON valid in PB/CC only), or as FALSE (zero) if compiling to DLL format (PB/Win only).

The equate %PB_EXE is always defined in PowerBASIC, so %DEF(%PB_EXE) will always be evaluated as TRUE. The difference being the value assigned to the equate by the compiler. See the examples below.

These can be used in conjunction with #IF as a compiler directive to selectively include or exclude code from the compiled file.

See also

#IF, Numeric Equates, Built-in numeric equates, String Equates, Built-in string equates

Example

‘ 1. Conditional compilation for PB/CC or PB/Win

#IF %DEF(%PB_CC32)

'Assume PB/CC

#COMPILE EXE "\PBCC\APPS\MYPROG.EXE"

#ELSE

'Assume PB/Win

#COMPILE DLL "MYAPP.DLL"

#ENDIF

' 2. Conditional compilation for EXE/CON or DLL

#IF %PB_EXE

' we are compiling to an EXE (PB/CC or PB/Win)

FUNCTION PBMAIN

...

END FUNCTION

#ELSE

' we are compiling to a DLL (PB/Win)

FUNCTION PBLIBMAIN

...

END FUNCTION

#ENDIF


#BLOAT metastatement

Purpose

Artificially inflate the disk image size of a compiled program.

Syntax

#BLOAT size_expression

Remarks

#BLOAT allows the creation of artificially bloated program files on disk, in order to match or exceed that generated by competing "BloatWare" compilers. #BLOAT does not affect the memory image size (running size) of a compiled program.

size_expression

The size_expression parameter is a simple Long-integer expression that specifies the total desired size of the compiled programs disk image, but is ignored if it is smaller than the actual program size. #BLOAT uses sections of the actual compiled code to fill and obfuscate the portion added to the file.

While #BLOAT adds no true merit to the technical efficiency of the compiled code, there are a number of reasons for its use, including:

  1. To allow "BloatWare" programmers to feel more comfortable when using PowerBASIC.

  2. To impress project leaders/managers with the volume of executable code created.

  3. To allay the fears of uninformed customers who may mistakenly infer that "such tiny programs couldn't possibly do everything that..."

  4. To make certain versions of a program more readily identifiable simply by examining the size of the file on disk.

  5. To improve convolution of the contents of the executable disk image, because the bloat region appears to contain executable code.

See also

#COMPILE EXE

Example

#BLOAT 1024 * 1024 * 4 ' Create a 4 MB EXE file


#COMPILE metastatement

Purpose

Determine what type of file will be created by the compiler.

Syntax

#COMPILE EXE ["filename{.exe}"]

Remarks

This metastatement specifies the type of file the compiler should produce. PB/CC can only generate EXE applications at this time. The #COMPILE metastatement can only be used once per program, and must be placed before any executable code.

You may, optionally, specify the target name and directory of the file. If this clause is omitted, the compiled file is given the name of the main source code file with an .EXE extension.

If a path is included, the compiled file is placed in the named directory; otherwise, it is placed in the current directory.

If the named directory does not exist, the filename is invalid or locked, if the EXE is still running, or if the file cannot be successfully stored in that location for some other reason, a compile-time Error 496 ("Destination file write error") occurs.

Restrictions

If #COMPILE is not specified in the module, the default is #COMPILE EXE.

See Also

#COMPILER, PBMAIN, WINMAIN


#COMPILER metastatement

Purpose

Define the compiler for this program.

Syntax

#COMPILER [PBCC | PBWIN] [version] [, [PBCC | PBWIN] [version]] [, ...]

Remarks

The #COMPILER metastatement defines the name, and optionally the version, of the compiler which should be used for this source program. This is particularly valuable if you use both PBCC and PBWIN, since an immediate error 408 (Wrong compiler for this program) will be generated if you mistakenly try to compile it with any other compiler or version.

If the optional version number is included, it must be specified as either the major version only, or major and minor with two decimal digits:

#COMPILER PBCC

#COMPILER PBCC 4

#COMPILER PBCC 4.03

In the first case, compilation with any version of PBCC would be acceptable. In the second case, compilation with any version of PBCC 4.xx would be acceptable, while in the third case, compilation with any compiler other than PBCC 4.03 would generate the compile-time Error 408 ("Wrong compiler for this program").

If the program is compatible with more than one compiler and/or version, multiple parameters may be listed, each separated by a comma. In this case, an error is generated only if there is no match with any of the parameters.

#COMPILER PBCC 4, PBCC 5

#COMPILER PBCC 4, PBWIN 8

In the first case, compilation with either PBCC 4 or PBCC 5 is acceptable (hypothetically, as there is no PB/CC 5 at this writing). In the second case, compilation with either PBCC 4 or PBWIN 8 is acceptable.

See Also


#CONSOLE metastatement

Purpose

Enable or disable auto-creation of a console window upon execution of the program.

Syntax

#CONSOLE [ON|OFF]

Remarks

#CONSOLE OFF can be used to create a program which doesn't need to output a visible result to the screen.

Restrictions

If you disable the console window, do not use commands that expect keyboard input, such as WAITKEY$, WAITSTAT, INPUT, or INKEY$. With no console from which to acquire input, these commands will simply lock up.

#CONSOLE ON|OFF must precede all executable code. If #CONSOLE is omitted, the default condition is #CONSOLE ON.

See also

Errors, Error Trapping, Debugging, #DEBUG PRINT


#DEBUG ERROR metastatement

Purpose

Control generation of error checking code

Syntax

#DEBUG ERROR [ON|+ | OFF|-]

Remarks

#DEBUG ERROR option specifies whether the compiler should generate code that checks for array boundary and null- errors wherever they may occur. The default setting is OFF.

When #DEBUG ERROR mode is ON, any attempt to access an array outside of its boundaries, or attempting to use a null-pointer will generate a run-time Error 9 ("Subscript/Pointer out of range"), and the statement itself is not executed.

When OFF, all statements are executed "as-is" and no errors are generated. However, accessing an array outside its boundaries or using a null-pointer can cause a General Protection Fault (GPF) or Exception error.

It is best to enable #DEBUG ERROR error checking when developing a program. Once all of the more obvious bugs have been eradicated, you will want to return to the default setting (OFF), as this will make your code smaller and faster. Depending on the type of application being developed, the final (production) version of a program may not need to contain any error-checking code.

Restrictions

#DEBUG ERROR is always enabled when code is running within the Debugger, regardless of any explicit #DEBUG ERROR metastatement.

Disk I/O errors are always caught, regardless of the state of #DEBUG ERROR.

#DEBUG ERROR ON does not trap array boundary errors of arrays within User-Defined Types and Unions. are only tested for null (zero) values. Non-zero pointer target addresses are not tested for readability or writeability.

See also

Errors, Error Trapping, Debugging, #DEBUG PRINT


#DEBUG PRINT metastatement

Purpose

Display information in the IDE's Debugger Output Window

Syntax

#DEBUG PRINT string_expression

Remarks

The PRINT option allows the programmer to display arbitrary information in the IDE's Debug Output Window during a debugging session. The output window is provided by debugger to display status information about the state of the debugging session; however, #DEBUG PRINT provides a convenient way of creating a "process log" of a Sub/Function/ variable as the program runs. Combined with FUNCNAME$, #DEBUG PRINT can be a useful tool for debugging application code. See the Example below.

This is possible because the Debugger Output Window has a scrollable range somewhat like a console window, whereas the Watch Window shows only the instantaneous value of a variable.

#DEBUG PRINT statements are ignored when code is compiled into a standalone (EXE/DLL) file; they are only included when using the Debugger. Control codes in the string are translated into hex format in the output window. For example, embedded CHR$(0) or $NUL bytes are displayed as "<00>".

See also

Debugging, #DEBUG ERROR, FUNCNAME$

Example

FUNCTION PBMAIN() AS LONG

Arg1% = 10000

Arg2% = 20000

CALL MySub(Arg1%, Arg2%)

CALL MySub(Arg2%, Arg1%)

#DEBUG PRINT "Done!"

END FUNCTION

SUB MySub(Arg1%, Arg2%)

#DEBUG PRINT "We're in " & FUNCNAME$

#DEBUG PRINT "Arg2% is" & STR$(Arg2%)

END SUB

Result

We're in MYSUB

Arg2% is 20000

We're in MYSUB

Arg2% is 10000

Done!


#DIM metastatement

Purpose

Specify if variables must be declared before use.

Syntax

#DIM {ALL | NONE}

Remarks

#DIM NONE (the default), requires you to dimension arrays, but not other kinds of variables, before their use.

Using #DIM ALL requires you to declare all variables before they are used in a program. This option makes PowerBASIC behave a lot like languages like C++ and Pascal which require that all variables be declared before they can be used. Although this will require more work, as even simple variables must be declared with DIM, LOCAL, GLOBAL, STATIC, or THREADED statements, it will protect you from subtle errors like misspelling a variable name. For example, if you are using a variable NumRecords in your program and write a line like:

INCR NumRecrods

PowerBASIC will detect that you're trying to use a previously undeclared variable (since NumRecrods is misspelled) and give you a compile-time error 519 ("Missing declaration"). If you hadn't specified #DIM ALL, you wouldn't have gotten an error, but your program would now have a bug that could be difficult to diagnose.

#DIM ALL means the same thing as OPTION EXPLICIT, and the two can be used interchangeably.

Restrictions

When #DIM ALL is used, type-specifier symbols with variable names are not allowed in a DIM var statement. e.g. Dim a$(10) will result in compile error 519. Instead variables or arrays defined with the DIM statement must use the the AS vartype format. Additionally, DEFtype statements, such as DEFINT, DEFLNG, etc. will be ignored, resulting in an error 519 where any variable they would otherwise define is used.

See also

DEFtype, DIM, GLOBAL, LOCAL, REDIM, STATIC, OPTION EXPLICIT

Example

#DIM ALL

...

DIM ListName(1 TO 400) AS STRING

...

FOR ix = 1 TO 10 ' PowerBASIC flags this line

' since "ix" wasn't dimensioned

ListName(ix) = "Test"

NEXT


#IF/#ELSEIF/#ELSE/#ENDIF metastatements

Purpose

Define sections of source code to be compiled or ignored, depending on a certain condition. This is often referred to as conditional compilation.

Syntax

#IF [NOT] {%equate | %DEF({%numeric_equate | $string_equate}) | expression}

{statements}

[#ELSEIF [NOT] {%equate | %DEF({%numeric_equate | $string_equate}) | expression}

{statements}]

[#ELSE

{statements}]

#ENDIF

Remarks

%equate is a named constant or constant value. The %DEF operator allows you to test whether an equate has been defined. %DEF returns TRUE or FALSE. Typical usage: #IF %DEF(%PB_DLL16) or #ELSEIF NOT %DEF(%PB_WIN32). expression may be a simple numeric expression using the arithmetic operators +, -, *, /, and \, and the relational operators >, <, >=, <=, <>, and =, and may also include the CVQ function.

PowerBASIC automatically defines the equates in the following table according to the PowerBASIC compiler being used. Please note the references to other PowerBASIC compilers are included for those writing programs that may be compilable by more than one PowerBASIC compiler.

Equate

Definition

%PB_CC32

Pre-defined as TRUE (non-zero) in PB/CC for Windows. Defined as FALSE (zero) in other compilers.

%PB_DLL16

Pre-defined as TRUE (non-zero) in PB/DLL 16-bit, FALSE (zero) in other compilers.

%PB_DLL32

Synonym of %PB_WIN32

%PB_WIN32

Pre-defined as TRUE (non-zero) in PB/Win 32-bit, but is not defined in other compilers.

%PB_REVISION

Pre-defined as the hex revision (8.00 = &H800).

%PB_REVLETTER

Pre-defined as the ASCII code of the revision letter (a = &H61), or &H20 if there is no revision letter.

%PB_EXE

Pre-defined as TRUE (non-zero) if compiling to EXE or CON format (CON valid in PB/CC only), or as FALSE (zero) if compiling to DLL format (PB/Win only).

The equate %PB_EXE is always defined in PowerBASIC, so %DEF(%PB_EXE) will always be evaluated as TRUE. The difference being the value assigned to the equate by the compiler.

Examples of valid expressions can include:

#IF %DEBUG = -1&

#IF %DEBUG AND (NOT %RELEASE)

#IF NOT %DEBUG

#IF %VERSION <> CVQ("DemoMode")

Note that the AND, OR and NOT operators work as bitwise operators, rather than logical operators, in #IF metastatements.

If the value of %equate or if %DEF(%equate|$equate) is TRUE (non-zero) or if the result of expression is TRUE, the statements between #IF and #ELSE or #ELSEIF are compiled, and the statements between #ELSE or #ELSEIF and #ENDIF are ignored.

If the value of %equate or %DEF(%equate|$equate) is FALSE (zero) or the result of expression is FALSE, the statements between #IF and #ELSE or #ELSEIF are ignored, and those between #ELSE or #ELSEIF and #ENDIF are compiled.

The #ELSE or #ELSEIF clause and associated statements are optional, but #ENDIF is required.

Conditional compilation statements can be nested up to 16 levels deep. A primary use of conditional compilation is to include test code in your programs that will be compiled during program development (but not in the final product), and to facilitate building special editions of an application from a single source code file.

It is possible to perform bitwise operations on to produce a TRUE/FALSE result. For example:

#IF (%PB_REVISION AND &H0FF00) - &H0700

SoftwareVersion$ = "not 7.x"

#ELSE

SoftwareVersion$ = "7.x"

#ENDIF

See also

%DEF operator, IF statement, IF block

Example

Example ' 1. Conditional compilation by equate value

%DEBUG = -1 'set to 0 for no debugging

#IF %DEBUG

CALL SubRoutine(Arg1, Arg2, Arg3, Answer)

CALL DisplayDebugData(Answer)

#ELSE

CALL SubRoutine(Arg1, Arg2, Arg3, Answer)

#ENDIF

' 2. Conditional compilation for EXE/CON or DLL

#IF %PB_EXE

' we are compiling to an EXE (PB/CC or PB/Win)

FUNCTION PBMAIN

...

END FUNCTION

#ELSE

' we are compiling to a DLL (PB/Win)

FUNCTION PBLIBMAIN

...

END FUNCTION

#ENDIF


#INCLUDE metastatement

Purpose

Instruct the compiler to read a text file from disk and treat it as an integral part of the source code.

Syntax

#INCLUDE "filespec"

Remarks

Use #INCLUDE to compile the text of another file along with the current file. filespec is a string constant that follows normal LFN file-naming conventions, and which names a PowerBASIC source code file. If filespec does not include an extension, the compiler looks for that file name with the default extension of .BAS.

If filespec does not include a path, the compiler scans the search path for each #INCLUDE file before checking the current (default) directory. For the IDE, the search path can be set in the Compiler Preferences tab in the Options dialog. The search path can also be specified when compiling from the command-line by using the /I Include option.

When the compiler encounters an #INCLUDE metastatement, it reads filespec from disk and continues compilation with the source code in filespec. When the end of filespec is reached, compilation continues with the statement immediately following the #INCLUDE in the original source file. The result is the same as if the contents of the included file were physically present within the original text. This allows large source files to be broken into smaller "units" that are more manageable.

#INCLUDE metastatements can be nested as many as six levels deep. That is, an included file can have #INCLUDE metastatements of its own, including files that also have #INCLUDE metastatements, and so on, for a total of six levels of files (including the primary file). Note that macros count as #include files for nesting purposes.

See also

WIN32API.INC Updates

Example

' MYHELLO.BAS

#INCLUDE "WIN32API.INC" 'include Windows API calls

FUNCTION PBMAIN

MessageBox 0, "Hello World!", "PowerBASIC", %MB_OK

END FUNCTION


#OPTION metastatement

Purpose

Set the minimum Windows version requirements for your program.

Syntax

#OPTION {VERSION3 | VERSION4 | VERSION5}

Remarks

The #OPTION metastatement controls the "minimum Windows version" tag that is written into your compiled code. If the version you select is equal or lower to the version of Windows that is running, the application will be executed. In turn, Windows will tailor the messages it sends to your program according to this version number, so your program will not need to handle messages from a later Windows version. The version tag may also affect the appearance and behavior of Windows common dialogs.

Conversely, if the version tag you select is higher than the version of Windows that is running, Windows will display an error message instead of running your application. For example, running a VERSION5 application on a VERSION4 platform would fail. It is your responsibility to make sure that your program only uses the Windows features that are present in the specified version of Windows. For example, don’t call an API that’s present only in Windows XP, if you want your program to run under Windows 98.

Use #OPTION VERSION3 to make the compiled output file require a minimum of Windows 95 or NT 3.1. That includes Windows 95, 98, ME, Windows NT 3.1-4.0, Windows 2000, XP, Windows 2003 (and later). Use #OPTION VERSION4 (default) to make the compiled output file require a minimum of Windows 95 or NT4. That includes Windows 95, 98, ME, Windows NT 4.0, Windows 2000, XP, Windows 2003 (and later). Use #OPTION VERSION5 to make the compiled output file require a minimum of Windows 2000. That includes Windows 2000, XP, Windows 2003 (and later).

Example

#OPTION VERSION5


#REGISTER metastatement

Purpose

Control automatic allocation of Register variables.

Syntax

#REGISTER {ALL | DEFAULT | NONE}

Remarks

Register variables may be Extended-precision floating-point variables, or 16/32-bit integer-class variables (Word, Dword, Integer, or Long). The #REGISTER metastatement determines the method of automatic allocation of Register variables.

The #REGISTER metastatement works at two levels - a "global" setting, and a "local" setting for each Sub/Function. To set the global default #REGISTER options, it must precede all executable code. To override the global register option for an individual Sub or Function, it must be placed between the FUNCTION/END FUNCTION and SUB/END SUB pairs before any executable code.

ALL

#REGISTER ALL requests automatic allocations of all possible Register variables, both integer-class and Extended-precision float variables.

DEFAULT

#REGISTER DEFAULT (default) requests automatic allocations of integer-class variables in all cases, and Extended-precision floating-point variables located in a Sub or Function which contains no reference to another Sub or Function.

NONE

#REGISTER NONE disables automatic assignment of Register variables. You can still use the REGISTER statement to explicitly define Register variables in your code on an individual basis. This provides a way to hand-optimize your code to help obtain the utmost performance.

See also

REGISTER, Optimizing your code

Example

#REGISTER DEFAULT ' global register setting

FUNCTION PBMAIN() AS LONG

#REGISTER NONE ' No automatic register

' vars in this function

REGISTER x& ' Explicitly declare x&

…

END FUNCTION


#RESOURCE metastatement

Purpose

Embed a PowerBASIC Resource (.PBR) file into the executable file.

Syntax

#RESOURCE "filespec"

Remarks

This metastatement is used to include (embed) a PowerBASIC resource (.PBR) file into your compiled program.

filespec

If filespec does not include a path, the compiler scans the search path the #RESOURCE file before checking the current (default) directory. For the IDE, the search path can be set in the Compiler Preferences tab in the Options dialog.

PowerBASIC metastatements support Long File Names (LFNs).

The search path can also be specified when using the Resource Compiler (RC.EXE) from the command-line by using the /i (INCLUDE) option -- see RC.HLP for more information.

To create a .PBR file, you must first create a resource script (.RC) which contains resource commands. A typical resource file to specify an Icon consists of just one line of text:

ICON1 ICON MYICON.ICO

This line of text would be saved as a .RC file, and then compiled into a .RES file with a suitable resource compiler. PowerBASIC resource files can be compiled directly from the PowerBASIC IDE. They can also be created using a resource compiler (such as the Microsoft™ Resource Compiler, RC.EXE) which converts a resource script (.RC) file into a binary resource (.RES) file, which is then processed by the PBRES.EXE utility to create a .PBR file.

Resource files can contain bitmaps, string data, custom binary data, version information, dialogs and a whole lot more. To create more complex resource files, a Resource Editor is highly recommended. PowerBASIC's PB/Win compiler includes a copy of the Microsoft Dialog Editor (DLGEDIT.EXE), and the Microsoft Resource Compiler (RC.EXE).

The PowerBASIC IDE can be used to compile a Resource Script (.RC file) into PowerBASIC resource file format (a .PBR file) without resorting to using command-line tools to compile resource files. For additional information on creating resource files, please review the Resource Files chapter.

Restrictions

PowerBASIC does not permit more than one #RESOURCE statement per module. If you need more than one, the usual way to accomplish this is to create a separate DLL for each additional resource file (using PowerBASIC's PB/Win compiler).

Note: If you are adding an icon resource to a Console mode application, please note that Windows 95's "Windows Explorer" will not display icons of console applications in file lists. This behavior was corrected in Windows 98, and later editions of Windows.

See also

Resource Files

Example

#RESOURCE "HELLO.PBR"


#STACK metastatement

Purpose

Set the maximum potential stack size.

Syntax

#STACK num_expr

Remarks

The literal numeric expression is expressed in bytes, and is rounded up to the next 64 Kb boundary. The minimum allowable stack size is 128 Kb, and a typical stack size of at least 1 Megabyte (the default) is usually recommended.

Upon program startup, an initial block of 128 Kb of physical memory is allocated to the stack. As the stack grows, additional memory is automatically added, as necessary, up to the specified maximum. Since physical memory is only committed as required, it is usually prudent to overestimate potential stack needs.

Restrictions

#STACK is meaningful with EXE (executable) files only.


#TOOLS metastatement

Purpose | Enable or disable integrated development tool code in compiled code.
Syntax | #TOOLS [ON|+ | OFF|-]
Remarks | The #TOOLS metastatement allows integrated development tools like TRACE, PROFILE, and CALLSTK to be readily disabled, ensuring that extra code and data is not compiled into the final (distribution) version of an application. #TOOLS defaults to ON, and may appear only once in the source code, before any statement that generates executable code.
See also | CALLSTK, CALLSTK$, CALLSTKCOUNT, FUNCNAME$, PROFILE, TRACE


ABS function

Purpose | Return the absolute value of a numeric expression.
Syntax | y = ABS(numeric_expression)
Remarks | The absolute value of a number is its non-negative value. For example, the absolute value of -3 is 3, and the absolute value of +3 is also 3. The absolute value of 0 is 0.
See also | SGN


ACODE$ function

Purpose

Translate a Unicode string into an ANSI .

Syntax

a$ = ACODE$(UnicodeStrExpression)

Remarks

ACODE$ returns the ANSI, multi-byte equivalent string of the Unicode string contained in UnicodeStrExpression. To convert an ANSI string into Unicode, use the UCODE$ function.

Unicode strings require two bytes to represent a Unicode character, whereas ANSI strings (the native PowerBASIC string format) use one byte to represent a character. Therefore, ACODE$ returns a string that has half of the byte count of the Unicode string, yet represents the same number of characters.

See also

UCODE$


AND operator

Purpose

The AND operator works as both a logical and a bitwise arithmetic operator.

Syntax

p AND q

Using AND as a logical operator

AND returns TRUE (non-zero) if (and only if) both its operands are TRUE. The AND truth table looks like this:

Truth Table
x | y | x AND y
T | T | T
T | F | F
F | T | F
F | F | F

Using AND as a bitwise arithmetic operator

AND masks clear selected bits of an integer-class value without affecting the other bits. For example, to clear the most-significant (leftmost) 2 bits in the integer value &H9700, AND it with &H3FFF. That is, the mask contains all 1s, except for the bit positions you want to force to 0:

See also

Arithmetic Operators, EQV, IMP, ISFALSE, ISTRUE, NOT, OR, XOR


ARRAY ASSIGN statement

Purpose | Allow the assignment of a number of values to successive elements of an array.
Syntax | ARRAY ASSIGN array() = param1 [,param2] [,…]
Remarks | ARRAY ASSIGN allows the assignment of a number of values to successive elements of an array. The assignment always starts with the first array element, and continues sequentially as the elements appear in memory. The values to be assigned must match the array type, and may be literals, variables, or expressions.
See also | ARRAY DELETE, ARRAY INSERT, ARRAY SCAN, ARRAY SORT, DIM, LBOUND, REDIM, UBOUND, Array Data Types
Example | ARRAY ASSIGN x&() = 1,2,3,4,5,6,7,8,9,10


ARRAY DELETE statement

Purpose

Delete a single item from a given array.

Syntax

ARRAY DELETE array([index]) [FOR count] [, expression]

Remarks

ARRAY DELETE deletes the data stored at the nominated element in array, an n-dimensional array. You can specify the index of the element which is to have its data deleted, how many elements (count) are to be automatically shifted down by one position, and what data value to give the last element after the rest of the elements have been shifted (expression).

All of these parameters are optional. If index is not specified, the data stored in the element at the beginning of the array is deleted. If expression is not present, the last element that the data is shifted out of will contain zero if array is a numeric array, or an empty string if array is a string array. If a shift count is given, when shifting the rest of the array to eliminate the element, only count elements will be shifted.

By default, ARRAY DELETE throws away the data at the element index of array, shifting the data in the appropriate portion of the array to cover the old element:

DIM A(1 TO 4) AS LONG

ARRAY DELETE A(2), 17&

makes A(2)=A(3), A(3)=A(4), and A(4)=17. The original value of A(1) remains in place. Use count to "protect" a portion of the array from the shift:

DIM A(1 TO 4) AS LONG

ARRAY DELETE A(2) FOR 2, 17&

makes A(2)=A(3) and A(3)=17 because you told it to shift only 2 elements. The original values of A(4) and A(1) remain in place.

DELETE with multi-dimensional arrays

count can also be used with a multi-dimensional array (stored in linear column-major order; see ARRAY SORT), to prevent shifting element data from one dimension into another dimension, thus preserving the organization of the array. For example:

DIM A(0 TO 1,0 TO 1) AS INTEGER

A(0,0)=0

A(1,0)=100

A(0,1)=200

A(1,1)=300

ARRAY DELETE A(0,0) FOR 2, 17%

makes A(0,0)=100 and A(1,0)=17. The original values of A(0,1) and A(1,1) remain in place since you told it to shift only 2 elements. Without count:

ARRAY DELETE A(0,0), 17%

makes A(0,0)=100, A(1,0)=200, A(0,1)=300, and A(1,1)=17. The original value of A(1,1) is lost.

Restrictions

ARRAY DELETE cannot be used on arrays within UDT structures. However, ARRAY DELETE can be used with arrays of UDT structures - simply treat them as if they were an array of fixed-length strings.

To use ARRAY DELETE on an embedded UDT array, use DIM..AT to dimension a regular array (of the same type) directly "over the top" of the UDT array, and use ARRAY DELETE on that array. For example:

TYPE SalesType

OrderNum AS LONG

PartNumber(1 TO 20) AS STRING * 20

END TYPE

...

DIM Sales AS SalesType

...

DIM Temp(1 TO 20) AS STRING * 20 AT VARPTR(Sales.Partnumber(1))

ARRAY DELETE Temp(5), "string"

ERASE Temp()

See also

ARRAY ASSIGN, ARRAY INSERT, ARRAY SCAN, ARRAY SORT, DIM, LBOUND, REDIM, UBOUND, Array Data Types

Example

Makes A(2)=3 and A(3)=2.5. A(0) and A(1) remain in place:

DIM A(0 TO 3) AS CUX

A(0)=0

A(1)=1

A(2)=2

A(3)=3

ARRAY DELETE A(2), 2.5@@

Makes A(0)=2, A(1)=3, and A(2)=0. The original value of A(0) is lost:

DIM A(0 TO 2) AS EXT
A(0)=1
A(1)=2
A(2)=3
ARRAY DELETE A()


ARRAY INSERT statement

Purpose

Insert a single item into a given array.

Syntax

ARRAY INSERT array([index]) [FOR count] [, expression]

Remarks

ARRAY INSERT inserts a single data item into array, an n-dimensional array. You can specify the index at which the new element data is to be inserted, how many elements (count) are to be shifted up by one position to make room for the new element data, and what data value to give the new element (expression).

All of these parameters are optional. If index is not specified, the element data is inserted at the beginning of the array If expression is not present, the new element will contain zero if array is a numeric array, or an empty string if array is a string array.

If a shift count is given, when shifting the rest of the array to make way for the new element data, only count elements will be shifted.

By default, ARRAY INSERT throws away the data in last element of array, then shifts the appropriate portion of the array to make way for the new element data:

DIM A(1 TO 4) AS LONG

ARRAY INSERT A(2), 17

makes A(4)=A(3), A(3)=A(2), and A(2)=17. The original value of A(4) is lost, while the original value of A(1) remains in place. Use count to "protect" a portion of the array from the shift:

DIM A(1 TO 4) AS LONG

ARRAY INSERT A(2) FOR 2, 17

makes A(3)=A(2) and A(2)=17 because you told it to shift only 2 elements. The original values of A(4) and A(1) remain in place.

INSERT with multi-dimensional arrays

count can also be used with a multi-dimensional array (stored in linear column-major order; see ARRAY SORT), to prevent shifting data from one dimension into another dimension, and thus preserving the organization of the array. For example:

DIM A(0 TO 1,0 TO 1) AS SINGLE

A(0,0)=0

A(1,0)=100

A(0,1)=200

A(1,1)=300

ARRAY INSERT A(0,0) FOR 2, 17

makes A(0,0)=17 and A(1,0)=0. The original values of A(0,1) and A(1,1) remain in place since you told it to shift only 2 elements. Without count:

ARRAY INSERT A(0,0), 17

mkes A(0,0)=17, A(1,0)=0, A(0,1)=100, and A(1,1)=200. The original value of A(1,1) is lost.

Restrictions

ARRAY INSERT cannot be used on arrays within UDT structures. However, ARRAY INSERT can be used with arrays of UDT structures - simply treat them as if they were an array of fixed-length strings.

To use ARRAY INSERT on an embedded UDT array, use DIM..AT to dimension a regular array (of the same type) directly "over the top" of the UDT array, and use ARRAY INSERT on that array. For example:

TYPE SalesType

OrderNum AS LONG

PartNumber(1 TO 20) AS STRING * 20

END TYPE

...

DIM Sales AS SalesType

...

DIM Temp(1 TO 20) AS STRING * 20 AT VARPTR(Sales.Partnumber(1))

ARRAY INSERT Temp(5), "string"

ERASE Temp()

See also

ARRAY ASSIGN, ARRAY DELETE, ARRAY SCAN, ARRAY SORT, DIM, LBOUND, REDIM, UBOUND

Example

Makes A(3)=2.5 and A(4)=3. A(0), A(1), and A(2) remain in place:

DIM A(0 TO 4) AS DOUBLE

A(0)=0

A(1)=1

A(2)=2

A(3)=3

ARRAY INSERT A(3), 2.5#

Makes A(0)=0, A(1)=1, and A(2)=2. The original value of A(2) is lost:

DIM A(0 TO 2) AS QUAD

A(0)=1

A(1)=2

A(2)=3

ARRAY INSERT A()


ARRAY SCAN statement

Purpose

Scan all or part of an array for a given value.

Syntax

Numeric array:

ARRAY SCAN array([index]) [FOR count], expression, TO lvar&

String arrays:

ARRAY SCAN array ([index]) [FOR count] [, FROM start TO end] [, COLLATE {UCASE |

cstring}], expression, TO lvar&

Remarks

ARRAY SCAN scans all or part of array, an n-dimension array, for the first element that satisfies expression. expression consists of a relational operator (=, >, <, <>, >=, =>, <=, =<) followed by an expression of the same data type as array. The relative index of the first match is stored in lvar&, which must be a Long-integer variable:

ARRAY SCAN A&(), > 5, TO I&

This line of code identifies the relative index of the first element of array A&() that is greater than 5, and stores the relative index in I&. The match index ranges from 1 to the last element of the scan + 1.

Since it is a relative index:

DIM A(1 TO 10) AS SINGLE

ARRAY SCAN A(), > 17.42!, TO I&

…will store 2 in I& if A(2) > 17.42, but:

DIM A(5 TO 20) AS SINGLE

ARRAY SCAN A(), > 17.42!, TO lvar&

…will store 2, not 6, in lvar& if A(6) > 17.42.

If none of the scanned elements satisfy expression, zero will be stored in lvar&.

Together, index and count specify the portion of array to be scanned. index specifies the element at which the scan is to begin, while count specifies the number of consecutive elements to be scanned. If index is not specified, the scan begins at the first element of array. If count is not specified, the array is scanned from element index to the last element of array. If neither is specified, the entire array is scanned:

DIM A&(1 TO 100)

ARRAY SCAN A&(5), =1, TO I& 'scans 5..100

ARRAY SCAN A&() FOR 10, =1, TO I& 'scans 1..10

ARRAY SCAN A&(10) FOR 20, =1, TO I& 'scans 10..29

ARRAY SCAN A&(), =1, TO I& 'scans 1..100

Scanning a string array

When scanning a string array, COLLATE UCASE treats all lowercase letters as uppercase during the scan (for example, element "Bob" would satisfy the condition = "BOB"):

ARRAY SCAN A$(), COLLATE UCASE, = "BOB", TO I&

' scans A$() for "BOB"; all letters treated as

' uppercase

COLLATE string is used to specify a non-standard scanning order. cstring must contain exactly 256 characters, in the order in which they should be compared, from lowest to highest. For example, the normal ascending ASCII scan order (where "A" is considered less than "B", etc.) would be described by a string containing ASCII codes 0 through 255 in order:

C$ = CHR$(0 TO 255)

ARRAY SCAN A$(), COLLATE C$, > "BOB", TO I&

The normal descending ASCII scan order would be described by a string containing the reverse of the above:

C$ = STRREVERSE$(CHR$(0 TO 255))

ARRAY SCAN A$(), COLLATE C$, > "BOB", TO I&

The COLLATE string option is provided as a flexible means with which to specify a descending scan, or to specify the scanning order for strings containing international characters or other special symbols.

See ARRAY SORT for more information on building collating strings.

When scanning a string array, all characters of each element of the array are normally considered when performing comparisons. To limit the comparison to a specific subset of characters, use FROM to specify the start position, and TO to specify the end position that ARRAY SCAN will consider within each array element. For example, you could scan based on the zip code contained in the last 5 characters of a 40-character address string:

ARRAY SCAN A$(), = "90210", TO I&

' considers all characters when scanning for "90210"

ARRAY SCAN A$(), FROM 36 TO 40, = "90210", TO I&

' considers positions 36..40 only when scanning

Scanning a multi-dimensional array

When scanning a multi-dimensional array, the array is treated as a single-dimension array containing all of the elements of the multi-dimensional array, in linear column-major order. That is, all elements where all dimensions (except the first), are held at their minimum bounds, will come first in memory. These are immediately followed by the elements where the second dimension is set to its next consecutive index value, etc.

For example, the elements of a two-dimensional array (DIM A(0 TO n, 0 TO x)) would be stored in consecutive memory locations as follows:

A(0,0), A(1,0), …, A(n,0) ' The first n+1 elements,

A(0,1), A(1,1), …, A(n,1) ' The next n+1 elements,

… ' Subsequent elements,

A(0,x), A(1,x), …, A(n,x) ' The last n+1 statements.

…or more clearlty:
   A(0,0), A(1,0)…, A(n,0), A(0,1), A(1,1)…, A(n,1), A(0,x),
   A(1,x) …, A(n,x)

In this case, ARRAY SCAN A(0,0) FOR n+1, >5, TO I& would scan only elements (0,0)...(n,0), while ARRAY SCAN A(0,0), >5, TO I& would scan the entire array: elements (0,0)...(n,x). As mentioned earlier, since ARRAY SCAN records the relative index of the matched element, ARRAY SCAN A(0,0), >5, TO I& would store 2 in I& if A(1,0) > 5.

Options

The options for ARRAY SCAN can be specified in any order, as long as the FOR option, if present, directly follows the closing parenthesis of the name of array.

Restrictions

ARRAY SCAN cannot be used on arrays within UDT structures. However, ARRAY SCAN can be used with arrays of UDT structures - simply treat them as if they were an array of fixed-length strings.

To use ARRAY SCAN on an embedded UDT array, use DIM..AT to dimension a regular array (of the same type) directly "over the top" of the UDT array, and use ARRAY SCAN on that array. For example:

TYPE SalesType

OrderNum AS LONG

PartNumber(1 TO 20) AS STRING * 20

END TYPE

...

DIM Sales AS SalesType

...

DIM Temp(1 TO 20) AS STRING * 20 AT VARPTR(Sales.Partnumber(1))

ARRAY SCAN Temp(), FROM 1 TO LEN(Search$), = Search$, TO lResult&

ERASE Temp()

See also

ARRAY ASSIGN, ARRAY DELETE, ARRAY INSERT, ARRAY SORT, DIM, LBOUND, REDIM, UBOUND, Array Data Types

Example

ARRAY SCAN A&(5) FOR 10, > 64000&, TO B&

Scans elements 5 through 14 of array A&, looking for the first element whose value is > 64000, and stores the relative index of that element in B&.

ARRAY SCAN A$(5) FOR 10,FROM 16 TO 25,COLLATE C$, = D$, TO B&

Scans elements 5 through 14 of array A$, looking only at characters 16 to 25 of each element, using the order specified by collating string C$, looking for the first element whose value is equal to D$, and stores the relative index of that element in B&.


ARRAY SORT statement

Purpose

Sort all or part of a given array.

Syntax

Numeric array:

ARRAY SORT darray([index]) [FOR count] [,TAGARRAY tarray()] [,{ASCEND | DESCEND}]

String array:

ARRAY SORT dArray([index]) [FOR count] [,FROM start TO end] [,COLLATE {UCASE |

cstring}] [,TAGARRAY tarray()] [,{ASCEND | DESCEND}]

Remarks

ARRAY SORT sorts all or part of darray, an n-dimensional array, in ascending or descending order. tarray is a tag-along array whose elements are swapped in the same order as those in darray as the sort proceeds (you could sort an array of names and have an array of corresponding addresses tag along, for example). tarray must have at least as many elements as darray, since corresponding elements of tarray will be swapped during the sort.

Note that tarray does not have to be of the same type as darray. For example, you could have a numeric array containing account numbers tag along with a string array containing user names:

DIM Users$(100 TO 500), AcctNum&(100 TO 500)

ARRAY SORT Users$(), TAGARRAY AcctNum&()

Together, index and count specify the portion of darray to be sorted. index specifies the element at which the sort is to begin, while count specifies the number of consecutive elements to be sorted. If index is omitted, the sort begins at the first element of darray. If count is omitted, the array is sorted from element index to the last element of darray. If both are omitted, the entire array is sorted:

DIM A&(1 TO 99)

ARRAY SORT A&(5) 'sorts elements 5..99 of A&

ARRAY SORT A&() FOR 10 'sorts elements 1..10 of A&

ARRAY SORT A&(9) FOR 20 'sorts elements 9..28 of A&

ARRAY SORT A&() 'sorts elements 1..99 of A&

Sorting numeric arrays

By default, arrays are sorted in ascending order. To sort in descending order, include the DESCEND keyword:

ARRAY SORT A&(), DESCEND ' descending order

ARRAY SORT A&(), ASCEND ' ascending order

ARRAY SORT A&() ' ascending order

Sorting string arrays

When sorting a string array, the sort is performed in ascending order by default. In addition to DESCEND, ARRAY SORT provides the COLLATE UCASE and COLLATE string options.

COLLATE UCASE treats all lowercase letters as equal to their uppercase counterparts during the sort (elements "Bob" and "BOB" would be considered equal, for example):

DIM A$(1 TO 5)

A$(1) = "Bob"

A$(2) = "Jan"

A$(3) = "Linda"

A$(4) = "Ann"

A$(5) = "Jerry"

ARRAY SORT A$(), COLLATE UCASE, DESCEND

'sorts A$() in descending order; case-insensitive

COLLATE cstring is used to specify an entirely new sorting order. This can be used for a variety of purposes, the most obvious of which is the case of international character sets. The collate string cstring must contain exactly 256 characters, one for each of the ASCII codes 0-255, in the order that they would be sorted (from lowest to highest, if an ascending sort were performed on them).

Each position in the string represents the ASCII code of that value. The contents of the byte at that position tell PowerBASIC the "weight" or importance factor of that particular ASCII code. The default is that position 0 has a weight of 0, position 1 has a weight of 1, etc, so that CHR$(0) sorts first, CHR$(1) sorts next, and so on through CHR$(255).

Suppose you want the special character "ä" to have the same weight as the standard character "a". It's easy: construct a string of 256 characters, 0-255; then go to the position of "ä" (ASCII code 132), and change the contents of that byte so it is exactly equal to the code for "a" (97). The following code fragment constructs just such a collate string:

' Create a 256-character string:

FOR ix = 0 TO 255

C$ = C$ + CHR$(ix)

NEXT

MID$(C$, 132 + 1) = CHR$(97)

We add one to the ASC value for MID$ because string positions start at 1, not 0. We can also use the expanded CHR$ function to create the same collating string using less code:

C$ = CHR$(0 TO 131, 97, 133 TO 255)

It is most important to remember the rule for creating a collating string, as it is easy to make an intuitive jump to the wrong conclusion. Each position in the string (1-256) represents the ASCII code with that value minus one (CHR$(0) to CHR$(255)). The contents of the byte at that position tell the ARRAY SORT procedure the new "weight" or importance factor for that particular code. This is exactly the technique used by the 80x86-assembler opcode XLAT.

Suppose you want CHAR$(0) to sort at the very end of the sequence. To do that, you would set the byte at position 0+1 to CHR$(255) and the bytes at positions 0+2 to 0+256 to the values 0 to 254. The ASCII sequence in the collating string would appear like this: 255,0,1,2,3,4…254. Using the expanded CHR$ function, this is straightforward:

C$ = CHR$(255, 0 TO 254)

To sort upper case and lower case alphabetic characters as exactly equal, just set positions 97 to 122 (a-z) to the values 65-90 (A-Z). This is precisely how COLLATE UCASE is handled. With the collating method implemented by this procedure in PowerBASIC, it is possible for two or more ASCII codes to have equal "weight".

As mentioned earlier, many programmers make a common, fatal mistake by intuitively creating a collating string that is simply a list of ASCII codes, in the sequence they wish to sort. That is, they expect the byte which appears first in the string to sort first, the byte which appears next to sort second, so that creating a collate string from the BASIC code:

CHR$(65) + CHR$(66) + CHR$(67) + ...

…might cause the characters "ABC..." to be sorted first. This technique will never work with the ARRAY statement and must be carefully avoided. We describe it here only because it is a common error. While it is arguably more intuitive than the technique implemented in PowerBASIC, the reason it does not work is that it doesn't allow two or more ASCII codes to have the same "weight".

The following code builds a collating string compatible with the American OEM ASCII character set. For the fastest operation, this code should be run only once and the collating string should be made global.

GLOBAL cu AS STRING

FOR x = 0 TO 255

cu = cu + CHR$(x)

NEXT

MID$(cu, 97+1, 26) = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"

MID$(cu, 129+1, 6) = "ueaaaa" ' üéâäàã

MID$(cu, 136+1, 9) = "eeeiiiAAE" ' ëèïîìÄÂÉ

MID$(cu, 147+1, 8) = "ooouuyOU" ' ôöòûùÿÖÜ

MID$(cu, 161+1, 5) = "iounN" ' ìòùñÑ

MID$(cu, 168+1, 1) = "?" ' ¿

{ your code goes here }

ARRAY SORT MyArray$(), COLLATE cu

An alternative arrangement using the expanded CHR$ function may look like this:

cu = CHR$(0 TO 96, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", _

123 TO 128, "ueaaaa", _ ' üéâäàã

135, "eeeiiiAAE", _ ' ëèïîìÄÂÉ

145 TO 146, "ooouuyOU", _ ' ôöòûùÿÖÜ

155 TO 160, "iounN", _ ' ìòùñÑ

166 TO 167, "?", _ ' ¿

169 TO 255)

For example, the normal ascending ASCII sort order would be described by a string containing ASCII codes 0 through 255 in order:

C$ = CHR$(0 TO 255)

ARRAY SORT A$(), COLLATE C$

The normal descending ASCII sort order would be described by a collating string containing the reverse of the above:

C$ = STRREVERSE$(CHR$(0 TO 255))

ARRAY SORT A$(), COLLATE C$

COLLATE string can also be used with the ASCEND or DESCEND option. With ASCEND, the sort is performed in the order specified by COLLATE string; DESCEND sorts using the reverse of the order specified by COLLATE string:

ARRAY SORT A$(), COLLATE C$, DESCEND

The COLLATE string option is provided as a flexible means with which to specify the sorting order for strings containing international characters or other special symbols. Please keep in mind that the characters with ASCII code above CHR$(127) may have different meanings in different countries. The examples here assume that the default American OEM ASCII code page is in use.

When sorting a string array, all characters of each element of the array are normally considered when performing comparisons. To limit the comparison to a specific subset of characters, use FROM to specify the start position, and TO to specify the end position that ARRAY SORT will consider within each array element. For example, you could sort based on the zip code contained in the last 5 characters of a 40-character address string:

ARRAY SORT A$() ' sorts all chars

ARRAY SORT A$(), FROM 36 TO 40 ' sorts 36 - 40 only

By using the FROM..TO keywords, it also becomes possible to sort an array of User-Defined Types. In this case, ARRAY SORT can sort the array as if it were an array of fixed-length strings.

Sorting a multi-dimensional array

When sorting a multi-dimensional array, the array is treated as a single-dimension array containing all of the elements of the multi-dimensional array, in linear column-major order. That is, all elements where all dimensions (except the first), are held at their minimum bounds, will come first in memory. These are immediately followed by the elements where the second dimension is set to its next consecutive index value, etc.

For example, the elements of a two-dimensional array (i.e., DIM A(n,x)) would be stored in consecutive memory locations like this:

(0,0), …, (n,0), (0,1), …, (n,1), …, (0,x), …, (n,x)

In this case, ARRAY SORT A(0,0) FOR n+1 would sort only elements (0,0)...(n,0), while ARRAY SORT A(0,0) would sort the entire array: elements (0,0)…(n,x).

Be very careful when using ARRAY SORT with multi-dimensional arrays so as not to disrupt the organization of the data in the arrays.

Options

The options for ARRAY SORT can be specified in any order, as long as the FOR option, if it is present, directly follows the closing parenthesis of the name of darray.

Restrictions

ARRAY SORT cannot be used on arrays within UDT structures. However, ARRAY SORT can be used with arrays of UDT structures - simply treat them as if they were an array of fixed-length strings.

To use ARRAY SORT on an embedded UDT array, use DIM..AT to dimension a regular array (of the same type) directly "over the top" of the UDT array, and use ARRAY SORT on that array. For example:

TYPE SalesType

OrderNum AS LONG

PartNumber(1 TO 20) AS STRING * 20

END TYPE

...

DIM Sales AS SalesType

...

DIM Temp(1 TO 20) AS STRING * 20 AT VARPTR(Sales.Partnumber(1))

ARRAY SORT Temp()

ERASE Temp()

See also

ARRAY ASSIGN, ARRAY DELETE, ARRAY INSERT, ARRAY SCAN, CHR$, DIM, LBOUND, REDIM, UBOUND, Array Data Types

Example

A&(5) FOR 10, TAGARRAY B$(), DESCEND

Sorts elements 5 through 14 of array A& in descending order, tagging along elements 5 through 14 of array B$.SCAN, CHR$, DIM, LBOUND, REDIM, UBOUND

ARRAY SORT A#()

Sorts all elements of array A# in ascending order, using no tag-along array.

ARRAY SORT A$(5) FOR 10, FROM 16 TO 25, COLLATE C$, TAGARRAY D()

Sorts elements 5 to 14 of array A$, considering only characters 16 to 25 of each element, using the sort order specified by collating string C$, tagging along elements 5 to 14 of array D.

ARRAY SORT A$()

Sorts all elements of array A$ in ascending order, considering all characters of each element, using no tag-along array.


ARRAYATTR function

Purpose

Return descriptive attributes of a given array.

Syntax

y = ARRAYATTR(Arr(), AttrNum)

Remarks

ARRAYATTR returns various descriptive attributes of an array, depending upon the value of AttrNum

AttrNum |  |  | Definition
0 |  |  | Returns TRUE (-1) if the array is currently dimensioned, FALSE (0) if not.
1 |  |  | Returns the data type, as defined in the following table. Note that the numeric equates listed on the right of the table are built into PowerBASIC, but the numeric values they represent are subject to change. Therefore, application code should always use the numeric equates rather than the numeric value, to ensure compatibility with future versions of PowerBASIC. The current data type definitions are:
 |  | Type | Array type | Keyword | Equate
 |  | 0 | Byte | BYTE | %VARCLASS_BYT
 |  | 1 | Word | WORD | %VARCLASS_WRD
 |  | 2 | Double-word | DWORD | %VARCLASS_DWD
 |  | 4 | Integer | INTEGER | %VARCLASS_INT
 |  | 5 | Long-integer | LONG | %VARCLASS_LNG
 |  | 6 | Unsigned Bit | n/a | %VARCLASS_BIT
 |  | 7 | Signed bit | n/a | %VARCLASS_SBT
 |  | 8 | Quad-integer | QUAD | %VARCLASS_QUD
 |  | 10 | Single-precision | SINGLE | %VARCLASS_SNG
 |  | 11 | Double-precision | DOUBLE | %VARCLASS_DBL
 |  | 12 | Extended-precision | EXT | %VARCLASS_EXT
 |  | 13 | Currency | CURRENCY | %VARCLASS_CUR
 |  | 14 | Extended Currency | CURRENCYX | %VARCLASS_CUX
 |  | 17 | Variant | VARIANT | %VARCLASS_VRNT
 |  | 18 | Interface | INTERFACE | %VARCLASS_IFAC
 |  | 19 | GUID | GUID | %VARCLASS_GUID
 |  | 20 | UDT or Union | TYPE/UNION | %VARCLASS_TYPE
 |  | 21 | ASCIIZ string | ASCIIZ/ASCIZ | %VARCLASS_ASC
 |  | 22 | Fixed-length string | STRING * n | %VARCLASS_FIX
 |  | 23 | Dynamic string | STRING | %VARCLASS_STR
 |  | 24 | Field string | FIELD | %VARCLASS_FLD
 | 2 |  | Returns TRUE (-1) if it is an array of pointers, FALSE (0) if not.
 | 3 |  | Returns the number of dimensions of the array. The lower and upper boundaries of each dimension can be retrieved with the LBOUND and UBOUND functions respectively
 | 4 |  | Returns the total number of elements in the array. For example, the array DIM A&(3,4,5) would comprise 120 elements (4 x 5 x 6 = 120).
 | 5 |  | Returns the array element size. For example, an array of Double-precision variables would be 8 bytes. For dynamic strings, the size of the string handle (4 bytes) is returned. For DISPATCH and INTERFACE arrays, ARRAYATTR returns the size of a pointer variable (4 bytes).

See also

DIM, LBOUND, UBOUND, REDIM

Example

DIM z(3,4,5) AS CURRENCYX

DIM x AS LONG, Answer AS STRING

FOR x = 0 TO 5

Answer = Answer + FORMAT$(x)

Answer = Answer + $TAB

Answer = Answer + FORMAT$(ARRAYATTR(z(),x))

Answer = Answer + $CRLF

NEXT x

' The results are stored in Answer:

Result

0 -1

1 14

2 0

3 3

4 120

5 8


ASC function

Purpose

Return the ASCII code of the specified character in a string.

Syntax

y = ASC(string_expression [, position&])

Remarks

ASC returns the ASCII code (0 to 255) of the specified character of string_expression. The first character of the string is position one, followed by position two, etc. If position& is not specified then the first character is presumed. If position& is a negative number, ASC counts from the end of the string back toward the front. For example, if position& is -1 then the last character is returned. If position& is -2 then the second from last character is returned, and so on.

The acronym ASCII stands for American Standard Code for Information Interchange, a standardized code in which the numbers between 0 and 127 are used to represent the upper and lowercase letters, the numerals 0 to 9, punctuation marks, and other symbols used in data communication. PCs employ an extended version of ASCII, which contains codes 0 through 255. The "upper" 128 codes (128-255) are not standard ASCII codes, and may differ from one country to another, and from code page to code page.

CHR$ does the reverse of ASC - it produces the one-character string corresponding to its ASCII code argument.

Restrictions

If the string passed is null (zero-length) or the position is zero or greater than the length of the string, the value -1 is returned.

See also

ASC statement, CHR$

Example

x$ = "The ASCII value of A is" + STR$( ASC("A") )

Result

The ASCII value of A is 65


ASC statement

Purpose | Place an ASCII byte at the specified position in a string.
Syntax | ASC(string, position&) = byte_expression
Remarks | ASC places the ASCII code (0 to 255) of the specified character code byte_expression into string at position&. The first character of the string is position one, followed by position two, etc. If position& is negative, ASC counts from the end of the string. For example, if position& is -2, the second to last character in string is modified. See the ASC function for more information on ASCII codes.
Restrictions | The ASC statement cannot be used to extend the length of a string. If string is null (zero-length), or position is zero or greater than the length of string, the operation is ignored.
See also | ASC function, CHR$, MID$ statement
Example | A$ = "hello There" ASC(A$,1) = 72 'replace 1st character with an "H"


ASM statement

Purpose

Identify an assembly-language statement. PowerBASIC's Inline Assembler supports 8086/8088, 80286, 80386, 80486, Pentium, Floating-Point and MMX instructions.

Syntax

{! | ASM} opcode

Remarks

This statement allows you to place assembly-language code within your PowerBASIC source code. An exclamation mark (!) serves as a shortcut for the ASM keyword.

Each group of ASM statements must preserve the following CPU registers if the assembler code causes them to change: ESI, EDI, ESP, EBP, and all segment registers.

No other statements may appear on the same line as an ASM statement; however, comments are acceptable.

! DB 1, 2, 3, 4, 5

! DD &H45001222, &HFFFF4327

Any variable referenced in an assembly-language statement must be defined prior to use. For example:

x% = 10

! MOV AX, x%

You cannot access the target of a pointer with a single ASM statement as you might do in BASIC source code. Instead, you must use the pointer address indirectly. To simulate the BASIC statement INCR @x, you would write:

DIM x AS INTEGER PTR

ASM MOV EAX, x ; EAX holds a pointer to

; an Integer

ASM INC WORD PTR [EAX] ; Add one to target value

String literals of up to four characters may be used in Inline Assembler code:
! MOV AL, "a" ; move char a into reg AL

! MOV AX, "ab" ; move chars ab into reg AX

! ; "a" into AL, "b" into AH

! MOV EAX, "abcd" ; move chars abcd into reg EAX

PowerBASIC recognizes either an apostrophe ( ' ) or a semi-colon ( ; ) to specify a comment after a line of assembler code:

! PUSH EAX ; save the EAX register

! PUSH EBX ' save the EBX register

Restrictions

Care should be exercised to ensure registers are appropriately preserved when Inline Assembler code is intermixed with BASIC statements. See Saving Registers for more information.

See also

The Inline Assembler

Example

To add the values a&, b&, and c&, you would write:

LOCAL a&, b&, c&, z&

! MOV EAX, a&

! ADD EAX, b&

! ADD EAX, c&

! MOV z&, EAX

Notes

The follow lists outline the supported mnemonics, data types, operators, and registers that can be used with the ASM statement.

The ASM statement supports the following mnemonics:

AAA, AAD, AAM, AAS, ADC, ADD, AND

BOUND, BSF, BSR, BSWAP, BT, BTC, BTR, BTS

CALL, CBW, CCWD, CDQ, CLC, CLD, CLI, CMC, CMP, CMPSB, CMPSD, CMPSW, CMPXCHG, CPUID, CWDE

DAA, DAS, DEC, DIV

EMMS

F2XM1, FABS, FADD, FADDP, FCHS, FCLEX, FCOM, FCOMP, FCOMPP, FCOS, FDECSTP, FDIV, FDIVP, FDIVR, FDIVRP, FFREE, FIADD, FICOM, FICOMP, FIDIV, FIDIVR, FILD, FIMUL, FINCSTP, FINIT, FIST, FISTP, FISUB, FISUBR, FLD, FLD1, FLDCW, FLDENV, FLDL2E, FLDL2T, FLDLG2, FLDLN2, FLDPI, FLDZ, FMUL, FMULP, FNCLEX, FNINIT, FNLDCW, FNOP, FNSAVE, FNSTCW, FNSTENV, FNSTSW, FPATAN, FPREM, FPREM1, FPTAN, FRNDINT, FRSTOR, FSAVE, FSCALE, FSIN, FSINCOS, FSQRT, FST, FSTCW, FSTENV, FSTP, FSTSW, FSUB, FSUBP, FSUBR, FSUBRP, FTST, FUCOM, FUCOMP, FUCOMPP, FWAIT, FXAM, FXCH, FXTRACT, FYL2X, FYL2XP1

HLT

IDIV, IMUL, IN, INC, INSB, INSD, INSW, INT, INTO, IRET, IRETD

JA, JAE, JB, JBE, JC, JE, JECXZ, JG, JGE, JL, JLE, JMP, JNA, JNAE, JNB, JNBE, JNC, JNE, JNG, JNGE, JNL, JNLE, JNO, JNP, JNS, JNZ, JO, JP, JPE, JPO, JS, JZ

LAHF, LAR, LDS, LEA, LEAVE, LES, LFS, LGS, LOCK, LODSB, LODSD, LODSW, LOOP, LOOPE, LOOPNE, LOOPNZ, LOOPZ, LSL, LSS

MOV, MOVD, MOVQ, MOVSB, MOVSD, MOVSW, MOVSX, MOVZX, MUL

NEG, NOP, NOT

OR, OUT, OUTSB, OUTSD, OUTSW

PACKSSDW, PACKSSWB, PACKUSWB, PADDB, PADDD, PADDSB, PADDSW, PADDUSB, PADDUSW, PADDW, PAND, PANDN, PCMPEQB, PCMPEQD, PCMPEQW, PCMPGTB, PCMPGTD, PCMPGTW, PMADDWD, PMULHW, PMULLW, POP, POPA, POPAD, POPF, POPFD, POR, PSLLD, PSLLQ, PSLLW, PSRAD, PSRAW, PSRLD, PSRLQ, PSRLW, PSUBB, PSUBD, PSUBSB, PSUBSW, PSUBUSB, PSUBUSW, PSUBW, PUNPCKHBW, PUNPCKHDQ, PUNPCKHWD, PUNPCKLBW, PUNPCKLDQ, PUNPCKLWD, PUSH, PUSHA, PUSHAD, PUSHF, PUSHFD, PXOR

RCL, RCR, REP, REPE, REPNE, REPNZ, REPZ, RET, RETF, RETN, ROL, ROR

SAHF, SAL, SAR, SBB, SCASB, SCASD, SCASW, SETA, SETAE, SETB, SETBE, SETC, SETE, SETG, SETGE, SETL, SETLE, SETNA, SETNAE, SETNB, SETNBE, SETNC, SETNE, SETNG, SETNGE, SETNL, SETNLE, SETNO, SETNP, SETNS, SETNZ, SETO, SETP, SETPE, SETPO, SETS, SETZ, SHL, SHLD, SHR, SHRD, STC, STD, STI, STOSB, STOSD, STOSW, SUB

TEST

VERR, VERW

WAIT

XCHG, XLAT, XLATB, XOR

The ASM statement supports the following data types and operators:

BYTE

DB, DD, DW, DWD, DWORD

FAR

NEAR

POINTER, PTR

QWD, QWORD

SHORT

TBY, TBYTE

WORD, WRD

The ASM statement supports the following registers:

Integer

32-bit | Low 16-bit | High 8-bit | Low 8-bit
EAX | AX | AH | AL
EBX | BX | BH | BL
ECX | CX | CH | CL
EDX | DX | DH | DL
ESI | SI |  |
EDI | DI |  |
ESP | SP |  |
EBD | BD |  |

Segments

CS, DS, ES, SS, FS, GS

MMX Registers

MM(0), MM(1), MM(2), MM(3), MM(4), MM(5), MM(6), MM(7)

MM0, MM1, MM2, MM3, MM4, MM5, MM6, MM7

Floating Point registers

ST(0), ST(1), ST(2), ST(3), ST(4), ST(5), ST(6), ST(7)


ATN function

Purpose

Return the arctangent of an argument.

Syntax

y = ATN(numeric_expression)

Remarks

ATN returns the arctangent (Inverse Tangent) of numeric_expression; that is, the angle whose tangent is numeric_expression.

The result, as with all operations involving angles in PowerBASIC, is in radians rather than degrees. Although it is common to specify angles in degrees, the radian is a more convenient measurement for mathematical operations. One radian is defined as the angle at the center of a circle that subtends an arc equal in length to one radius. Since for all circles, using the constant p:

Circumference / radius = 2 * p

the length of the circumference of a circle is equal to 2 * p * radius, and the angle of a full circle (360 degrees) is equal to 2 * p radians.

To convert radians to degrees, just multiply the radian value by 180/p, or 57.29577951308232##. For example, the arctangent of 0.23456 can be converted this way:

t = ATN(.23456!) 't = 0.230395 (radians)

t = 57.29577951308232## * ATN(.23456!) 't= 13.200 (degrees)

To convert degrees to radians, multiply by 0.0174532925199433##. For example:

14 degrees = (0.0174532925199433## * 14) = 0.2443460952792062

radians

Rather than memorizing the radians/degrees conversion factors, calculate them for yourself by remembering this relationship: 2 p radians equals a full circle (360 degrees), so 1 p radian is 180 / p degrees. Conversely, 1 degree equals p / 180 radians.

p is a transcendental constant, meaning that it has an infinite number of decimal places. To 15-place accuracy, adequate for most applications, p = 3.141592653589793##. This value can be closely approximated with the expression:

pi## = 4 * ATN(1)

Degrees-to-radians and radians-to-degrees conversions are good applications for user-defined functions.

The ATN function always returns an Extended-precision result.

The Tangent (TAN) of a value can be easily calculated with the TAN function.

The Hyperbolic Tangent (TANH) can be calculated:

TanH = (EXP(2 * Value) - 1) / (EXP(2 * Value) + 1)

The Inverse Hyperbolic Tangent (ARCTANH) of a value can be calculated:

ArcTanH = LOG((1 + Value) / (1 - Value)) / 2

' Useful Macro functions

MACRO Pi = 3.141592653589793##

MACRO DegreesToRadians(dpDegrees) = (dpDegrees*0.0174532925199433##)

MACRO RadiansToDegrees(dpRadians) = (dpRadians*57.29577951308232##)

See also

COS, SIN, TAN


BEEP statement

Purpose | Sound a tone through the computer's speaker.
Syntax | BEEP
Remarks | BEEP plays the default Windows waveform sound, typically a ¼ second tone, through either the built-in speaker; or a sound card if installed (in which case the Windows "Default Beep" sound is played). The Default Beep can be configured in the Sounds section of Control Panel.
Restrictions | The physical aspects of the built-in speaker may have an effect on the quality and level of the resultant sound.


BGR function

Purpose

Convert an RGB color value to BGR format.

Syntax

bgrColor& = BGR(rgbColor&)

Remarks

An RGB value is a long integer value in the range of 0 to &H00FFFFFF. It is used to specify a precise color to various PowerBASIC functions and Windows API functions. The lowest three bytes of the value each specify the intensity of a primary color which combine to form the resultant color. Byte 0 (lowest) represents the red component, byte 1 the green, and byte 2 the blue. They can each take on a value in the range of 0 to 255. Byte 3 (highest) is always 0. The RGB function can be used to easily create an RGB value from the three component values.

Some Windows API functions, such as those which reference Device Independent Bitmaps (DIB), require that the colors be specified in the reverse sequence (Blue-Green-Red instead of Red-Green-Blue). In order to maximize performance and execution speed, PowerBASIC statements and functions which reference these structures also use the BGR format. These include GRAPHIC GET BITS and GRAPHIC SET BITS.

The BGR function translates an RGB value to its BGR equivalent by swapping the first and third bytes and returning the result. Because of the nature of the translation, calling BGR a second time converts a BGR value back to its original RGB format.

For example, the RGB value of red is &H0000FF. BGR translates it to &HFF0000. Calling it a second time converts the value back to H0000FF..

See also

RGB


BIN$ function

Purpose

Return a that is the binary (base 2) representation of its argument.

Syntax

s$ = BIN$(numeric_expression [, digits])

Remarks

numeric_expression must be in the range -2,147,483,648 to +2,147,483,647. Any fractional part of numeric_expression is rounded before the string is created. If digits is specified, the resulting string will be of length digits. If the resulting string is longer than digits, the result will be truncated from the left as needed. If the resulting string is shorter than digits, leading zeros will be added to the left to make the final string digits long.

Thus, for 12959:

0011 0010 1001 1111 ' +12959

1100 1101 0110 0000 ' reverse all bits

+1 ' add 1

1100 1101 0110 0001 ' -12959

Due to the use of two's-complement binary form to represent numbers, bit patterns with the most significant bit set to 1 may represent either of two possible values, depending on whether the value is regarded as signed or unsigned. See Numeric Literals for more information on explicitly casting numeric literal values to specific data types and distinguishing between signed and unsigned values. Also see the HEX$ function.

For example, the value with 32 binary ones "11111111111111111111111111111111" may be either -1 (signed Long-integer) or 4,294,967,295 (unsigned Double-word).

Note that the binary representation for a negative number is different for numbers that have different bit lengths. For a regular 16-bit Integer, -1 is represented as 16 binary ones ("1111111111111111"). This corresponds to the unsigned value 65,535. If you store this value in a 32-bit Long-integer, you would end up with either -1 or 65,535 depending on how you store it.

Binary strings can be converted to numeric values with the VAL function by prefixing the binary string with "&B". If the string has a leading zero, the result is always unsigned. For example:

a$ = BIN$(65535) ' = "1111111111111111"

x& = VAL("&B" + a$) ' Signed result (-1)

y& = VAL("&B0" + a$) ' Unsigned result (65535)

See also

FORMAT$, HEX$, OCT$, STR$, USING$, VAL


BIT CALC statement

Purpose

Set or reset a bit in an variable (or implied bit-array) based upon the result of an expression.

Syntax

BIT CALC intvar, bitnumber, calcexpr

Remarks

BIT CALC performs like a combination of the BIT SET and BIT RESET statements, offering the choice between set (1) and reset (0) according to the result of a numeric expression.

intvar

An integer class variable (Byte, Word, Double-word, Integer, Long-integer or Quad-integer), or a variable forming the base of an implied bit-array.

bitnumber

An integer class expression or numeric literal that specifies the bit number to adjust. Bit numbers start from zero (0), and extend to the size of the target variable or bit-array. For example, a 16-bit integer variable uses the range 0 to 15. An implied bit-array comprised of a Long-integer array with 100 elements (4 bytes * 100 = 400 bytes = 3200 bits) covers the bit range 0 to 3199.

calcexpr

The value derived from bit zero of calcexpr determines the set or reset action. If bit zero contains a zero (0), the bit in intvar is reset; if bit zero in calcexpr contains a one (1), the bit in intvar is set. This action can be more easily remembered if we consider PowerBASIC performs an implied bitwise AND operation (calcexpr AND 1) to derive the set or reset action.

Care must be exercised to ensure that the bit index number (bitnumber) does not exceed the number of bits that can be validly accessed. For example, reading the 17th bit of a 16-bit scalar variable may trigger a General Protection Fault (GPF). Similarly, adjusting the 4097th bit of a bit-array derived from a 128-element DWORD array may cause similar problems. bitnumber is always zero-based, so the 129th bit of an implied bit-array is referenced in the BIT statement with bitnumber equal to 128. For example: x& = BIT(A?(1), 128).

The first bit is the least-significant bit, which is bit number zero. For example:

See also

BIT function, BIT statement, BITS functions

Example

Example DIM dwStatus1 AS DWORD

DIM dwStatus2 AS DWORD

DIM iBit AS INTEGER

DIM sResult1 AS STRING

DIM sResult2 AS STRING

FOR iBit = 0 TO 31

BIT CALC dwStatus1, iBit, RND(0,1)

BIT CALC dwStatus2, iBit, iBit MOD 3

NEXT iBit

sResult1 = BIN$(dwStatus1,32)

sResult2 = BIN$(dwStatus2,32)

Result

sResult1 = "01001101001110101110111010010101"

sResult2 = "10010010010010010010010010010010"


BIT function

Purpose

Return the value of a particular bit in an integer-class variable (or in an implied bit-array)

Syntax

flag = BIT(intvar, bitnumber)

Remarks

The BIT function is used to determine the value of one particular bit in an integer-class variable or implied bit-array.

intvar

The parameter intvar must be a variable, not an expression. The BIT function returns either 0 or 1 to indicate the value of the specified bit.

bitnumber

The bit in question. The allowable range for the parameter is the same as that of a Long-integer. This makes it possible to have implicit bit-arrays of more than 2 billion bits in size. For such arrays, bits 0 to 15 are in the first word starting at intvar, bits 16-31 are in the next word, and so forth.

Implied bit-arrays are considered to start at the memory position of the variable intvar. For example, if intvar is itself an array variable, it is possible to access bits in any of the following elements of the array. See the array examples below.

Care must be exercised to ensure that the bit index number (bitnumber) does not exceed the number of bits that can be validly accessed. For example, reading the 17th bit of a 16-bit scalar variable may trigger a General Protection Fault (GPF). Similarly, adjusting the 4097th bit of a bit-array derived from a 128-element DWORD array may cause similar problems. bitnumber is always zero-based, so the 129th bit of an implied bit-array is referenced in the BIT statement with bitnumber equal to 128. For example: x& = BIT(A?(1), 128).

The first bit is the least-significant bit, which is bit number zero. For example:

See also

BIT CALC statement, BIT statement, BITS functions

Example

x% = 7

y% = BIT(x%, 2)

...

DIM z%(1:2000000) ' 32 million element bit-array

y% = BIT(z%(1),16) ' bit 0 of 2nd word of z%()

y% = BIT(z%(2000000),15) ' MSB of last element

y% = BIT(z%(1), 31999999&) ' MSB of last element


BIT statement

Purpose

Manipulate individual bits of an integer-class variable (or in an implied bit-array), for storing values such as TRUE/FALSE (flag) settings quickly and efficiently.

Syntax

BIT {SET | RESET | TOGGLE} intvar, bitnumber

Remarks

intvar must be one of the integer-class variable types: Byte, Word, Integer, Double-word, Long-integer, or Quad-integer.

The allowable range for the parameter bitnumber is the same as that of a Long-integer, making it possible to have implicit bit-arrays of more than 2 billion bits in size. Bits 0 to 15 are in the first word starting at intvar, bits 16-31 are in the next word, and so forth.

Implied bit-arrays are considered to start at the memory position of the variable intvar. For example, if intvar is itself an array variable, it is possible to access bits in any of the following elements of the array. See the array examples below.

Care must be exercised to ensure that the bit index number (bitnumber) does not exceed the number of bits that can be validly accessed. For example, adjusting the 17th bit of a 16-bit scalar variable may cause a subtle memory corruption problem, and/or may trigger a General Protection Fault (GPF). Similarly, adjusting the 4097th bit of a bit-array derived from a 128-element DWORD array may cause similar problems. bitnumber is always zero-based, so the 129th bit of an implied bit-array is referenced in the BIT statement with bitnumber equal to 128. For example: BIT SET A?(1), 128.

The first bit position is the least significant bit (LSB), which is bit number zero. For example:

SET

Sets the indicated bit to one.

RESET

Sets the indicated bit to zero.

TOGGLE

Toggles the indicated bit: one becomes zero; zero becomes one.

See also

BIT CALC statement, BIT function, BITS functions

Example

x% = 7

BIT SET x%, 2 ' Sets the 3rd bit (bit 2) to 1

BIT RESET x%, 10 ' Sets the 11th bit (bit 10) to 0

BIT TOGGLE x%, 5 ' Toggle bit 5

...

DIM z%(1 TO 2000) ' 32000 element bit-array

BIT SET z%(1), 37 ' Sets bit 5 of 3rd word to a 1

BIT TOGGLE z%(1),0 ' Toggle lowest bit in 1st word

BIT RESET z%(2000), 15 ' Clear the MSB of integer array element

' 2000 (bit 31999 of the implied bit array,

' numbered 0 to 31999)

BIT RESET z%(1), 31999 ' Clear the MSB of element 2000

' (this is equivalent to the previous line)


BITS function

Purpose

Return the least significant portion of an expression. Allows easy conversion between signed and unsigned expressions.

Syntax

resultvar = BITS(datatype, expression)

Remarks

Condenses the functionality of BITS%, BITS&, BITS?, BITS??, and BITS??? into a single function, and returns the least-significant bits of expression.

datatype

The parameter datatype may be BYTE, WORD, DWORD, INTEGER, or LONG.

expression

An integer class expression, variable, or numeric literal.

Restrictions

The BITS syntax provides for the addition of new data types in future versions of PowerBASIC, which may not support type-specifier characters. Existing code should be converted to the new syntax as soon as possible.

See also

BIT CALC statement, BIT function, BIT statement, BITS functions


BITS functions

Purpose

Return the least significant Byte, Word, Integer, Double-word, or Long-integer of an expression. Allows easy conversion between signed and unsigned expressions.

Syntax

bytevar = BITS?(expression)

wordvar = BITS??(expression)

dwordvar= BITS???(expression)

intvar = BITS%(expression)

longvar = BITS&(expression)

Remarks

The various BITS functions return the least-significant bits of expression, as shown in the following table:

Function

Accepts

Returns

BITS?

Long-integer

Byte (least-significant 8 bits, unsigned)

BITS??

Long-integer

Word (least-significant 16 bits, unsigned)

BITS???

Quad-integer

Double-word (least-significant 32 bits, unsigned)

BITS%

Long-integer

Integer (least-significant 16 bits, signed)

BITS&

Quad-integer

Long-integer (least-significant 32 bits, signed)

See also

BIT CALC statement, BIT function, BIT statement


CALL DWORD statement

Purpose

Invoke a procedure (Sub or Function) indirectly.

Syntax

CALL DWORD dwpointer [{BDECL | CDECL | SDECL} ()]
CALL DWORD dwpointer USING abc([arguments]) [TO result_var]

Remarks

CALL DWORD is an essential ingredient for implementing run-time (explicit) dynamic linking of DLLs, rather than the more common load-time (implicit) dynamic linking. This provides a way of constructing calls to APIs and DLLs that may not be present in all versions of Windows. This technique ensures that an application can start up successfully, even if Windows cannot resolve the API or DLL function. For more information on explicit linking, please review the Restrictions and Example code sections below.

The CALL DWORD statement has the following parts:

dwpointer

A Double-word, Long-integer, or pointer variable that contains the address of the entry point of a procedure (Sub or Function).

BDECL

Specifies that the declared procedure uses the BASIC/Pascal calling convention. When a procedure calls a BDECL procedure, it passes its parameters on the stack left to right.

It is the responsibility of the called procedure to clean up the stack before returning to the calling procedure. Therefore, all PowerBASIC Subs and Functions that specify the BDECL convention automatically clean up the stack before execution returns to the calling code.

CDECL

Specifies that the declared procedure uses the C calling convention. When a procedure calls a CDECL procedure, it passes its parameters on the stack right to left.

In addition, the calling procedure cleans up the stack upon return from the called procedure. When PowerBASIC code calls Subs and Functions using the CDECL convention, the stack is cleaned automatically after execution returns from the called code.

In the event the called procedure is imported or exported, PowerBASIC will automatically create a lowercase ALIAS, prefixed with an underscore. Any periods in the name are replaced with underscores at the same time.

The following shows how the ALIAS name would be created by using a DECLARE statement as the example format:

DECLARE SUB C_Function CDECL ()
DECLARE SUB C_Function CDECL ALIAS "_c_function" ()

When declaring a CDECL SUB or FUNCTION, you can specify trailing parameters as OPTIONAL, using a set of brackets [..]:

DECLARE SUB KerPlunk CDECL (x%, y% [, z%])

Note that the comma separating the y% parameter from the optional z% parameter is inside the brackets. The following calling sequences would then be valid:

CALL KerPlunk (x%, y%)
CALL KerPlunk (x%, y%, z%)

Optional parameters must be the last parameters designated in the list. The following is not valid:

DECLARE SUB KerPlunk CDECL ([x%,] y%, z%)

Because the SUB or FUNCTION being called does not know how many parameters are being passed at the time it is called, you should pass the number of parameters as one of the required parameters in the list.

SDECL

This is the default if neither BDECL nor CDECL are specified. SDECL (and its synonym STDCALL) specifies that the declared procedure uses the "Standard Calling Convention" as defined by Microsoft. When calling an SDECL procedure, parameters are passed on the stack right to left.

PowerBASIC Subs and Functions that use the SDECL/STDCALL convention automatically clean up the stack before execution returns to the calling code.

USING

This option is used to define a model procedure declaration which matches all of the calling conventions desired to be used to invoke the target Sub/Function. For example, the following two calls to the function MySubCall are equivalent:

DECLARE SUB MySubCall(Param1%, Param2%)
DIM PtrMySubCall AS DWORD
PtrMySubCall= CODEPTR(MySubCall)
...
CALL MySubCall(x1%, x2%)
CALL DWORD PtrMySubCall USING MySubCall(x1%, x2%)

arguments

An optional, comma-delimited list of variables, expressions, and constants to be passed to the procedure as parameters. In the CALL DWORD context, enclosing parentheses are required. The number and type of parameters passed must agree with the arguments of the procedure named by the USING clause. See CALL for more information on parameter passing methods.

For information on using optional parameters, please see DECLARE, FUNCTION and SUB topics.

TO result_var

When calling a Function (which returns a value), the TO keyword offers a way to assign the function return value to result_var. TO cannot be used without a USING clause also being used (and hence a DECLARE statement too), since PowerBASIC must be able to resolve the return data type.

If you call a Function through CALL DWORD with the USING option (but without a TO clause), the compiler can automatically discard the function result for you. However, this is safe only if the return value is of integer-class (Byte, Word, Integer, Long, and Dword). If the return value is a string or a floating-point value, you must employ the USING clause (in conjunction with an appropriate DECLARE statement) to avoid memory leaks.

Restrictions

A thread Function may not be directly called or executed, except by a THREAD CREATE statement.

DECLARE statements and the USING option

If you employ the USING option to specify a DECLARE statement that describes the parameter list, make sure the DECLARE statement does not include a LIB clause. Otherwise, the application will become linked to the DLL, just as if you had called the routine directly, rather than using CALL DWORD.

To solve the problem of accidental implicit-linking, remove the LIB and the ALIAS clauses from the DECLARE statement, and if necessary, rename the function to ensure it has a unique name (i.e., to avoid conflicts with DECLARE statements in WIN32API.INC, etc). For example, consider the following "standard" DECLARE statement:

DECLARE FUNCTION GetDiskFreeSpaceEx LIB "KERNEL32.DLL" ALIAS
"GetDiskFreeSpaceExA" (lpPath AS ASCIIZ, lpFreeToCaller AS
QUAD, lpTotalBytes AS QUAD, lpTotalFreeBytes AS QUAD) AS LONG

This particular API is not available on early versions of Windows 95; therefore, it is standard practice to use explicit (run-time) linking to call this function, if it is available. This is achieved through the LoadLibrary API (to determine if the DLL is available), followed by a call to GetProcAddress to obtain a code pointer to the actual API function (if the DLL includes the function).

Once a valid pointer has been obtained, the API can be directly called with the CALL DWORD statement, using a modified DECLARE to form a template for the USING option. The modified DECLARE may look like this:

DECLARE FUNCTION MyDiskFreeSpaceEx(lpPath AS ASCIIZ,

lpFreeToCaller AS QUAD, lpTotalBytes AS QUAD, lpTotalFreeBytes

AS QUAD) AS LONG

Please review the Example code section below to see this how this modified DECLARE statement is used with the CALL DWORD statement.

See also

CALL, CODEPTR, DECLARE, FUNCTION/END FUNCTION, SUB/END SUB, THREAD CREATE

Example

' Uses the DECLARE statement shown above

DIM hLib AS DWORD

DIM pAddr AS DWORD

DIM szDrv AS ASCIIZ * %MAX_PATH

DIM lResult AS LONG

szDrv = "C:\"

hLib = LoadLibrary("KERNEL32.DLL")

pAddr = GetProcAddress(hLib, "GetDiskFreeSpaceExA")

IF pAddr <> 0 THEN _

CALL DWORD pAddr USING MyDiskFreeSpaceEx(szDrv, FreeToUserQuota&&, SizeOfDisk&&, TotalFree&&) TO lResult

FreeLibrary hLib


CALL statement

Purpose

Invoke a procedure (Sub or Function).

Syntax

CALL ProcName [([arguments])] [TO result_var]

Remarks

The CALL statement has the following parts:

ProcName

The name of a Sub or Function defined elsewhere in the program.

arguments

An optional, comma-delimited list of variables, expressions, and constants to be passed to the procedure as parameters, for up to 32 parameters. If the CALL keyword is used, the arguments must be enclosed in parentheses. If no DECLARE statement is provided, the CALL keyword may be used to forward reference a SUB or FUNCTION that appears physically later in the source code than does the calling code.

As long as ProcName is a PowerBASIC procedure, or is an external procedure declared with the DECLARE statement, you can omit the CALL keyword. If you do so, you may omit the parentheses surrounding arguments. For example, the following lines are equivalent:

CALL MyProc(parm1, parm2)

MyProc(parm1, Parm2)

MyProc parm1, parm2

However, if the first parameter argument is enclosed in parentheses for any reason, the entire parameter list must be enclosed in parentheses. For example:

MyProc (3+z, b) ' Valid syntax

MyProc ((3+z), b) ' Valid syntax

MyProc (3+z), b ' Invalid syntax

This updated syntax now permits macros to be called using the SUB-style convention if/when the macros expand directly to Function calls. For example:

MACRO sm(Msg) = SendMessage(a, Msg, b, c)

…can be called like this (when the return value is not required):

sm(x)

In all cases, the number and type of parameters passed must agree with the arguments in procedure definition.

Passing parameters

In a procedure definition, every parameter is described by type and according to the way it is passed to the procedure. The type may be any normal variable type, such as , , , User-Defined Type, etc. The passing method describes how the value is presented to the procedure: by reference (BYREF), by value (BYVAL), or by reference to a copy (BYCOPY).

BYREF

When a parameter is passed by reference, it consists of a 4-byte address of the data. In this case, the original data can be modified by the procedure.

BYVAL

When a parameter is passed by value, it consists of an actual copy of the data. Since the parameter is a copy, the original data cannot be modified by the procedure.

When you pass parameters from the calling code with an explicit BYVAL, you effectively switch off the compilers type-checking for that parameter. This can be useful in cases where the called code is expecting a BYREF parameter, and you wish to pass an address of another data type that would trigger a compile-time error without the BYVAL method. For example:

SUB TheSub(x AS ASCIIZ) ' Address of x expected

...

DIM a$

a$ = "Dynamic string data"

CALL TheSub(BYVAL STRPTR(a$)) ' Pass data address

BYCOPY

A parameter passed by copy is a special case; somewhat of a hybrid of the other two methods. When a procedure expects a parameter to be passed by reference, it expects to see a to the data. In some cases, such as when the parameter is a calculated expression, it is not precisely possible to pass a pointer, since an expression result is a temporary value that does not exist in a permanent memory location. On the other hand, if you wish to ensure that the original data is not modified by the procedure, you can place a BYCOPY override in the arguments list.

In both cases, a copy of the data is stored in a temporary memory location, and the parameter consists of a 4-byte address of this temporary location. Another way to force BYCOPY is to enclose a variable name in parentheses, so it will appear to the compiler as an expression.

Unless declared otherwise, parameters default to BYREF passing method. Expressions and constants are always passed BYCOPY. Fixed length strings, User-Defined Types, and full arrays are always passed BYREF.

CALL MySub (i&) ' i& is passed by reference

CALL MySub (BYREF i&) ' i& is passed by reference

CALL MySub (BYCOPY i&) ' i& is passed by copy

CALL MySub ((i&)) ' i& is passed by copy

Entire arrays are specified by using an empty set of parentheses after the array, while individual array elements are specified by subscript index number. For example:

CALL SumArray(a()) ' pass entire array 'a'

CALL SumArray(a(3)) ' pass element 3 of array 'a'

The CALL statement can be used to invoke functions, as well as SUBs. In this case, the return value of the function is simply discarded, unless the TO keyword is used to specify a return variable.

If a SUB/FUNCTION expects a parameter by reference, it is possible to substitute a pointer by value, for the identical result. This is particularly useful with Fixed-length strings and Types:

DECLARE SUB a(z%)

DIM MyInt AS INTEGER, x AS INTEGER PTR

x = VARPTR(MyInt)

CALL a(MyInt)

' or

CALL a(BYVAL x)

' or

CALL a(BYVAL VARPTR(MyInt))

Of course, if the procedure is expecting a parameter by value, you may not pass the pointer, but rather the pointer target (i.e., CALL a(@x)).

PowerBASIC compilers have a limit of 32 parameters per SUB or FUNCTION. To pass more than 32 parameters, construct a User-Defined Type (UDT) and pass (the address of) the UDT by reference (BYREF) instead.

Fixed-length strings, ASCIIZ strings, and User-Defined Types/Unions may also be passed as BYVAL or OPTIONAL parameters, now. Try to avoid passing large items BYVAL, as it’s terribly inefficient, and there is a maximum size limit of 64 Kb for a given parameter list. Arrays cannot be passed BYVAL.

When a Sub/Function definition specifies either a BYREF parameter or a pointer variable parameter, the calling code may freely pass a BYVAL DWORD or a Pointer instead. While the use of the explicit BYVAL override in the calling code is optional, it is recommended for clarity. It is necessary to explicitly declare all pointer parameters as BYVAL (i.e., BYVAL X AS BYTE PTR). Failure to do so will generate a compile-time Error 549 ("BYVAL required with pointers").

A Sub/Function may also be imported and exported within the same module. That is, a function in the module may be stated as EXPORT, while a DECLARE in the same module specifies it as an imported function by the option LIB "XXX.DLL", provided that XXX.DLL is the name of the module. This may be particularly valuable when you wish to build an #INCLUDE file with all of the DECLARE statements for a project.

For information on using OPTIONAL parameters, please see DECLARE, FUNCTION, and SUB topics.

TO result_var

This offers an optional way to assign a function return value to result_var. For example, the following code assigns the return value to x% in two different ways:

x% = MyFunCall

CALL MyFunCall TO x%

Restrictions

A thread Function may not be directly called or executed, except by a THREAD CREATE statement.

See also

CALL DWORD, DECLARE, FUNCTION/END FUNCTION, SUB/END SUB, THREAD CREATE


CALLSTK statement

Purpose

Capture a complete representation of the stack frames in the call stack.

Syntax

CALLSTK diskfilename$

Remarks

PowerBASIC creates a stack frame for each call to a Sub or Function, and records each nested call in a call stack. The stack frame holds the parameters being passed to the routine, and providing space for local variable storage, etc. Since Subs and Functions can call other Subs and Functions to an almost limitless depth, there may be a substantial number of stack frames present at any given moment.

The CALLSTK statement can help provide answers to the age-old "how did I get here?" question. When combined with other debugging statements such as CALLSTK$, CALLSTKCOUNT, and TRACE, the programmer has a set of tools that can significantly reduce the amount of effort required to debug an application.

Executing a CALLSTK statement captures a representation of all of the stack frames that exist above the one that includes the CALLSTK statement. When the CALLSTK statement is executed, a standard sequential file (of the specified file name in diskfilename$) is created. The resulting disk file contains a list of every call to a Sub/Function, and their associated parameter values, which are currently defined on the call stack.

diskfilename$ must be a legal file spec, may be a Long File Name (LFN), and may include a path. If the file cannot be created for any reason, the operation will be ignored and no run-time error will be generated. If present, CALLSTK overwrites the existing file.

If PBMAIN calls the SUB aaa(x&) which then calls the SUB bbb(y&), the CALLSTK from within bbb(y&) might look like this:

PBMAIN()
aaa(77)
bbb(-1)

Later, if bbb(y&) exited, then aaa(x&) called ccc(z&), the updated CALLSTK from within ccc(z&) might then appear as:

PBMAIN()
aaa(77)
ccc(33)

Numeric parameters are displayed in decimal, while pointer and array parameters display a decimal representation of the offset of the target value.

Restrictions

CALLSTK can be invaluable during debugging, but it generates substantial additional code that should be avoided in a final release version of an application. If the source code contains #TOOLS OFF, all CALLSTK statements which remain in the program are ignored.

The CALLSTK statement is "thread-aware", displaying only stack frame details from the thread in which it was executed.

See also

#TOOLS, CALLSTK$, CALLSTKCOUNT, FUNCNAME$, PROFILE, TRACE

Example

FUNCTION PBMAIN

CALL Sb1(100)

END FUNCTION

SUB Sb1(x AS LONG)

CALL Sb2(x + 1)

END SUB

SUB Sb2(y AS LONG)

CALLSTK "Stack frame test.txt"

END SUB

Result

PBMAIN()
SB1(100)
SB2(101)


CALLSTK$ function

Purpose

Retrieve the details of a specific stack frame from the call stack.

Syntax

sfname$ = CALLSTK$( n )

Remarks

CALLSTK$(1) returns the name of the current Sub or Function, and the value of each of the parameters at the time it was called. CALLSTK$(2) returns the name of the Sub or Function which called the current one, as well as its parameters. Likewise, CALLSTK$(3) returns the one above it, and so forth.

If the CALLSTK$(n) parameter is outside the range of one (1) through the number of stack frames identified by CALLSTKCOUNT, an empty string is returned. Numeric parameters are displayed in decimal, while pointer and array parameters display a decimal representation of the offset of the target value.

Restrictions

The CALLSTK$ function can be invaluable during debugging, but it generates substantial extra code which should be avoided in a final release version of an application. If the source code contains #TOOLS OFF, all CALLSTK$ functions which remain in the program return an empty string.

The CALLSTK$ function is "thread-aware", returning only stack frame details from the thread in which it was referenced.

See also

#TOOLS, CALLSTK, CALLSTKCOUNT, FUNCNAME$, PROFILE, TRACE

Example

FOR x& = CALLSTKCOUNT TO 1 STEP –1
A$ = A$ + CALLSTK$(x&)
NEXT x&


CALLSTKCOUNT function

Purpose

Retrieve the number of stack frames in the call stack. Used in conjunction with the CALLSTK$ function.

Syntax

count& = CALLSTKCOUNT

Remarks

CALLSTKCOUNT returns a Long-integer value that represents the total number of stack frames that currently exist on the application call stack.

Retrieve individual stack frame details with the CALLSTK$ function, or write them all to a disk file with the CALLSTK statement.

Restrictions

The CALLSTKCOUNT function, when used in conjunction with the CALLSTK$ function, can be invaluable during debugging, but its use generates substantial extra code which should be avoided in a final release version of an application. If the source code contains #TOOLS OFF, all CALLSTKCOUNT functions which remain in the program return zero.

The CALLSTKCOUNT function is "thread-aware", returning only the stack frame count from the thread in which it was referenced.

See also

#TOOLS, CALLSTK$, CALLSTK, FUNCNAME$, PROFILE, TRACE

Example

FOR x& = CALLSTKCOUNT TO 1 STEP –1
A$ = A$ + CALLSTK$(x&)
NEXT x&


CBYT, CCUR, CCUX, CDBL, CDWD, CEXT, CINT, CLNG, CQUD, CSNG, and

CBYT, CCUR, CCUX, CDBL, CDWD, CEXT, CINT, CLNG, CQUD, CSNG, and CWRD functions

Purpose

Convert a value to specific variable type.

Syntax

bytevar? = CBYT(numeric_expression)

currencyvar@ = CCUR(numeric_expression)

currencyextvar@@ = CCUX(numeric_expression)

doublevar# = CDBL(numeric_expression)

doublewordvar??? = CDWD(numeric_expression)

extendedvar## = CEXT(numeric_expression)

integervar% = CINT(numeric_expression)

longintvar& = CLNG(numeric_expression)

quadintvar&& = CQUD(numeric_expression)

singlevar! = CSNG(numeric_expression)

wordvar?? = CWRD(numeric_expression)

Remarks

Each of these functions converts a numeric expression to a particular variable type. In each case, numeric_expression must be within the legal range for the result type. The numeric_expression will be rounded if necessary.

Function | Result type
CBYT | Byte
CCUR | Currency
CCUX | Extended-currency
CDBL | Double-precision floating-point
CDWD | Double-word
CEXT | Extended-precision floating-point
CINT | Integer
CLNG | Long-integer
CQUD | Quad-integer
CSNG | Single-precision floating-point
CWRD | Word

These conversion functions are rarely needed as PowerBASIC automatically performs any necessary conversions when executing an assignment statement or passing parameters. For example:

e% = f#

is equivalent to:

e% = CINT(f#)

In the case of the functions that convert to values, the result is rounded down if it has a fractional component of 0.5 or less. For example, CINT(1.5) returns 1, and CLNG(-0.6) returns -1.

See also

CEIL, CVI and associated functions, FIX, INT, MKI$ and associated functions

Example

' Calculate CINT for a series of values

FOR I! = 2.4! TO 2.65! STEP 0.05!

x$ = FORMAT$(I!, "0.00") + " is" + STR$(CINT(I!))

NEXT I!

Result

2.40 is 2

2.45 is 2

2.50 is 2

2.55 is 3

2.60 is 3

2.65 is 3


CEIL function

Purpose

Convert a variable or expression into an value, by returning the smallest integer value that is greater than or equal to its argument.

Syntax

intvar = CEIL(numeric_expression)

Remarks

The CEIL function rounds upward, returning the smallest integer value that is greater than or equal to numeric_expression. For example, y = CEIL(1.5) places the value 2 into y.

See also

CINT, FIX, FRAC, INT, ROUND

Example

' Display the ceiling for a series of values
FOR W! = -1.5! TO 1.5! STEP 0.5!
x$ = "CEIL" + FORMAT$(W!, "* 0.00") + _
" =" + FORMAT$(CEIL(W!), "* 0.00")
NEXT W!

Result

CEIL -1.50 = -1.00
CEIL -1.00 = -1.00
CEIL -0.50 = 0.00
CEIL 0.00 = 0.00
CEIL 0.50 = 1.00
CEIL 1.00 = 1.00
CEIL 1.50 = 2.00


CHDIR statement

Purpose

Change the current (default) directory on the default drive, or any other drive (similar to the DOS CHDIR command). CHDIR affects only the default drive for the current program.

Syntax

CHDIR path

Remarks

path is a string expression containing either a relative or an explicit directory name. The directory name can be constructed from a (DOS-Style) Short File Name (SFN) directory name, a Long File Name (LFN) directory name, or a combination of the two. Also, path may be prefixed with a drive letter and colon (i.e., "D:") to change the current directory on a non-default drive.

The current directory is the location where your program will perform file operations by default. Thus:

CHDIR "\DATA"

changes to the \DATA subdirectory on the current drive, and:

CHDIR "..\DATA2"

changes the current directory to a directory whose parent is also the parent to the original directory. The double-period implies the parent directory.

CHDIR "J:\Program Files\Internet Explorer"

changes the current directory of Drive J. Drive J need not be the current default drive.

If path does not specify a valid directory on the target drive, a run-time Error 76 occurs ("Path not found").

A program that changes the current directory on the default drive also changes its active directory.

path may also be used with UNC names (i.e., \\server\share), but their use is subject to operating system restrictions.

Restrictions

CHDIR is not intended to change the current default drive. Use CHDRIVE instead.

See also

CHDRIVE, CURDIR$, MKDIR, RMDIR


CHDRIVE statement

Purpose | Change the current default drive.
Syntax | CHDRIVE drive
Remarks | drive is a string expression whose first character is a letter from A to the highest logical drive letter. The trailing colon (:) that DOS uses is optional in PowerBASIC. If drive does not indicate a valid drive, a run-time Error 76 occurs ("Path not found").
See also | CHDIR, CURDIR$, MKDIR, RMDIR
Example | SDrive$ = "C" CHDRIVE SDrive$ ' change to the C: drive


CHOOSE function

Purpose

Return one of several values, based upon the value of an index.

Syntax

y = CHOOSE(index&, choice1 [, choice2] ...)
y& = CHOOSE&(index&, choice1 [, choice2] ...)
y$ = CHOOSE$(index&, choice1 [, choice2] ...)

Remarks

These functions take any number of choice arguments, and return the argument identified by index&. If index& evaluates to one, choice1 is returned, if two, choice2 is returned, etc. If index& is less than one or greater than the number of choices provided, zero or an empty is returned accordingly.

CHOOSE expects choices of any numeric type. CHOOSE& expects choices optimized for the Long-integer type. CHOOSE$ expects choices of string type. CHOOSE% is recognized as a valid synonym for CHOOSE&.

Restrictions

PowerBASIC only evaluates the selected choice at run-time, not all of them. This ensures optimum execution speed, as well as the elimination of unanticipated side effects.

See also

IIF, IIF&, IIF$, MAX, MAX&, MAX$, MIN, MIN&, MIN$, SWITCH, SWITCH&, SWITCH$, SELECT

Example

y& = 4

a$ = CHOOSE$(y&, "Bill", "Bob", "Bruce", "Barry")

Result

a$ = "Barry"


CHR$ function

Purpose

Convert one or more ASCII codes, ranges, and/or into a single string containing the corresponding ASCII character(s).

Syntax

s$ = CHR$(expression [,expression] [,...])
s$ = CHR$(string_expression [,...])
s$ = CHR$(x& TO y& [,...])

Remarks

CHR$ returns a string, each character of which represents the corresponding ASCII codes of the arguments. Single or multiple arguments may be specified, each of which may be a numeric character code expression, string expression, or numeric character code range. These argument types may be freely intermixed in a single CHR$ function. Numeric ASCII arguments must be in the range -1 through 255.

CHR$(x& TO y&) returns a sequence of all characters from CHR$(x&) through CHR$(y&) inclusive, provided x& <= y&. If x& > y&, an empty string is returned. For example, CHR$(65 TO 70) returns "ABCDEF".

The expanded CHR$ definition is intended to assist in the encoding of multi-byte strings, to avoid the need for concatenation operations. For example, the CHR$ function can be used to create COLLATE strings for the ARRAY SORT and ARRAY SCAN statements at run-time, and can be used to create string equates at compile time:

$colstring = CHR$(0 TO 131, 97, 133 TO 255)

The following lines are functionally equivalent, and return the same string result:

a$ = CHR$("Line1", 13, 10, "Line2")

a$ = "Line1" & CHR$(13) & CHR$(10) & "Line2"

a$ = "Line1" & $CRLF & "Line2"

CHR$ complements the ASC function, which returns the ASCII code of a nominated character in a string. In addition, CHR$ with a parameter value of -1 returns an empty string for that parameter. For example, CHR$(65, -1, 66) returns "AB".

CHR$ is also handy for creating characters that are difficult to enter at the keyboard, such as international characters for text output, or control code sequences for printer output, etc.

See also

ARRAY SCAN, ARRAY SORT, ASC function, ASC statement, NUL$, SPACE$, STRING$

Example

H$ = CHR$("a$=", $DQ, 33, $DQ+$DQ, 35 TO 39, 40, $DQ)

Result

a$="!""#$%&'("


CLOSE statement

Purpose

Conclude I/O (input/output) to / from a file or device.

Syntax

CLOSE [[#] filenum& [, [#] filenum&] ...]

Remarks

CLOSE ends the relationship between a PowerBASIC file number and the disk file or device that was associated with it by an OPEN statement. Any pending I/O operations on the file/device are concluded, buffers are flushed and released, and the disk directory information (if any) for that file is updated.

If no file number is specified, CLOSE closes all open files.

If the file was opened using OPEN HANDLE, the CLOSE statement is still needed, although it does not tell the operating system to close the file. In this special case, the file was already open when OPEN HANDLE provided access to it, and will remain open after CLOSE disassociates the file from PowerBASIC.

CLOSE works with all types of files and devices ( , devices, , , , etc).

The number symbols (#) are optional but recommended for clarity.

See also

COMM CLOSE, FILEATTR, FLUSH, OPEN, TCP CLOSE, UDP CLOSE


CLS statement

Purpose | Clear the active console page.
Syntax | CLS
Remarks | CLS clears the active console page to the current background color, and moves the caret to the upper left corner (row 1, column 1).
See also | COLOR, PAGE, PCOPY, PRINT


CLSID$ function

Purpose

Return a 16-byte GUID string (128-bit GUID format string) containing a CLSID associated with a unique ProgramID string of a COM object or component.

Syntax

a$ = CLSID$(ProgramID$)

Remarks

A CLSID string is a 128-bit (16-byte) binary string representing the GUID or UUID of a COM object/component. A CLSID string is not in a human-readable format.

You can convert textual ID name of a COM object/component into a CLSID string with the CLSID$ function. CLSID examines the system registry in order to determine the CLSID string associated with the ProgramID$ string.

The ProgramID$ parameter is not case-sensitive, so "MSAGENT.CONTROL.2", "MSAgent.Control.2" and "msagent.control.2" all refer to the same COM object/component. If the ProgramID$ cannot be found, or if any error occurs during the lookup and conversion process, CLSID$ will not set the ERR system variable, but will return an empty string.

To convert the binary CLSID string into human-readable GUID/UUID format, use the GUIDTXT$ function. CLSID$ is the complement to the PROGID$ function.

PowerBASIC programmers rarely, if ever, need to deal with CLSID strings in order to utilize a COM object or component.

a$

The return string may be assigned to a dynamic string, fixed-length or ASCIIZ string (at least 16 bytes long), or (typically) a GUID variable. See DIM for more information.

See also

DIM, GUID$, GUIDTXT$, INTERFACE/END INTERFACE, ISNOTHING, ISOBJECT, OBJECT, OBJACTIVE, OBJPTR, OBJRESULT, PROGID$, SET

Example

MSWordClassID$ = CLSID$("word.application.8")

IF LEN(MSWordClassID$) = 16 THEN

' Success getting the CLSID$ of MSWord

a$ = PROGID$(MSWordClassID$)

'a$ holds "Word.Application.8"

b$ = GUIDTXT$(MSWordClassID$)

'b$ holds "{000209FF-0000-0000-C000-000000000046}"

END IF


CODEPTR function

Purpose | Obtain a 32-bit address of a label, Sub or Function.
Syntax | address32 = CODEPTR({label | functionname | subname})
Remarks | CODEPTR is particularly useful when it is necessary to pass the address of a SUB or FUNCTION to Windows for callbacks. address32 must be a Long-integer (LONG) or Double-word (DWORD) variable.
Restrictions | Pointers may not be obtained for labels that are outside the scope of the current Sub or Function. Labels have local scope in current versions of PowerBASIC.
See also | STRPTR, VARPTR, CALL DWORD
Example | #COMPILE EXE SUB MySub() END SUB FUNCTION PBMAIN LOCAL MySubPtr AS LONG, X AS STRING MySubPtr = CODEPTR(MySub) ' Address of MySub() X = "MySub() is located at address " + _ FORMAT$(MySubPtr)) END FUNCTION


COLOR statement

Purpose

Set the foreground and background colors when displaying text in the active console page.

Syntax

COLOR [foreground], [background] [, count]

Remarks

foreground is an expression from 0 to 15, specifying the character color. background is an integer-class expression from 0 to 15, specifying the background color.

If either expression is out of range or missing, that value is left unchanged. Count is optional, and if included, causes the color attribute to be applied immediately to that number of character cells, starting at the current caret location. The caret is not moved, and the underlying characters are not changed.

Color attribute code table

value | Color
0 | Black
1 | Blue
2 | Green
3 | Cyan
4 | Red
5 | Magenta
6 | Brown
7 | White
8 | Gray
9 | Light blue
10 | Light green
11 | Light cyan
12 | Light red
13 | Light magenta
14 | Yellow
15 | High intensity white

Restrictions

The console window does not support flashing text.

See also

CLS, PAGE, PAGEACTIVE, PAGEVISIBLE, PCOPY, PRINT, SCREENATTR

Example

COLOR ,4 ' set background color to red


COMM CLOSE statement

Purpose

Close an open serial port.

Syntax

COMM CLOSE [#] hComm [, [#] hComm ...]

Remarks

Closes one or more communication ports, as specified by the PowerBASIC file number held in each hComm parameter. COMM CLOSE ends the relationship between a PowerBASIC file number, and the serial port device that was previously associated with it by the COMM OPEN statement.

The Number symbol (#) prefix is optional, but recommended for the purposes of clarity.

It is also recommended that you explicitly close any serial port that you have opened before your application terminates. Note that COMM CLOSE is a synonym for CLOSE.

See also

Serial Communications, CLOSE, COMM function, COMM LINE, COMM OPEN, COMM PRINT, COMM RECV, COMM RESET, COMM SEND, COMM SET

Example

COMM CLOSE #hComm, 5 ' Close hComm and file number 5


COMM function

Purpose

Retrieve the value or status of a communications parameter.

Syntax

lResult& = COMM([#] hComm, Comfunc)

Remarks

hComm is the PowerBASIC file number as was used by the COMM OPEN statement to open the communications port. Select a Comfunc keyword from the following table to retrieve the associated setting.

Comfunc

value (TRUE <> 0, FALSE = 0)

BAUD

Port Baud Rate (9600, 14400, 19200, etc).

BREAK

TRUE/FALSE Break is asserted. Break is generally used to "get the attention" of the connected modem, terminal or system.

BYTE

Number of bits per byte (4, 5, 6, 7, or 8).

CD

TRUE/FALSE Carrier Detect state; synonym for RLSD (READ-ONLY). When CD is TRUE, the DCE (modem) has a suitable connection on the communications channel present. When CD is FALSE, there is no suitable connection.

CTSFLOW

TRUE/FALSE Enable CTS output flow control (Input signal). When CTSFLOW is enabled, it causes the DTE (computer) to stop sending data whenever the CTS signal is set to logic low by the DCE (modem). Transmission continues when the DCE (modem) sets the CTS signal back to logic high. The CTS signal is usually used in response to an RTS signal.

DSRFLOW

TRUE/FALSE Enable DSR output flow control (Input signal). When DSRFLOW is enabled, it causes the DTE (computer) to stop sending data whenever the DSR signal is set to logic low by the DCE (modem). Transmission is enabled when the DSR signal returns to logic high. The DSR signal is often used in conjunction with CTS in response to a RTS signal.

DSRSENS

TRUE/FALSE Enable DSR sensitivity. When DSRSENS is enabled, data received by the DTE (computer) is placed into the receive buffer only if DSR is set to logic high. If DSR is set low, received data is discarded. Enabling DSRSENS allows DSR to enable or disable the DTE (the computer) to receive data from the DTE (the modem). DSRSENS is rarely used in practical communications situations.

DTRFLOW

TRUE/FALSE Enable DTR handshaking flow control (Output signal). When DTRFLOW is enabled, it signals that the DCE (modem) should prepare to connect to the communications channel. DTR is usually used for modem on-hook/off-hook control, but can also be used in conjunction with DSR for handshaking.

DTRLINE

TRUE/FALSE Enable DTR line. When enabled, DTRLINE leaves the DTR line active when the port is closed by the DTE (computer). This ensures that the DCE (modem) does not close the communications channel when the port is closed.

NULL

TRUE/FALSE Null ($NUL) bytes are discarded when read.

PARITY

TRUE/FALSE Enable parity checking. This mode must be enabled for the other Parity options to be selected.

PARITYCHAR

Character to use for parity error replacement. PARITY must be enabled.

PARITYREPL

TRUE/FALSE Enable character replacement on parity error. PARITY must be enabled.

PARITYTYPE

0 = None, 1 = Odd, 2 = Even, 3 = Mark, 4 = Space. PARITY must be enabled. Default = 0.

RING

TRUE/FALSE Ring indicator is on (READ-ONLY). When RING returns TRUE, a ringing signal is being received on the communications channel (by the modem). RING approximates the state of the ringing signal; however, it may not bereported accurately on all Windows platforms.

RLSD

Receive-line-signal-detect (READ-ONLY). See CD/Carrier Detect above.

RTSFLOW

Ready To Send (Output signal). 0 = Disable, 1 = Enable, 2 = Handshake, 3 = Toggle. Toggle is used for half-duplex (2-wire) operations to "reverse" the line. While the DTE (computer) is busy sending data, it raises the RTS signal and the DCE (modem) blocks its data receive channel. When RTS signal reverts to logic low, the DCE (modem) reverts to transmit mode and the DTE (computer) switches to receive mode.

Handshake mode causes the DTE (computer) to check the receive buffer (RXQUE) after each character is placed into the buffer. When the buffer is 5/6th full, the RTS signal is dropped. When the receive buffer drops to below 1/6th full, RTS is raised again

RXBUFFER

Size of the receive buffer in bytes.

RXQUE

Bytes currently in the receive buffer (READ-ONLY).

STOP

0 = 1 stop bits, 1 = 1.5 stop bits, 2 = 2 stop bits.

TXBUFFER

Size of the transmit buffer in bytes. In some cases, Windows may not be able to report the transmit size.

TXQUE

Bytes currently in the transmit buffer (READ-ONLY).

XINPFLOW

TRUE/FALSE Enable XON/XOFF input flow control. When the DTE (computer) receive buffer is full, an XOFF character is sent to the DCE (modem) to instruct it to halt transmission. When the DCE is ready to resume transmission, an XON character is sent to the DCE. Typically, XOFF is sent when the receive buffer has less than 1/16th remaining, and XON is sent when the receive buffer drops to less than 1/16th of its maximum size. Default = FALSE.

XOUTFLOW

TRUE/FALSE Enable XON/XOFF out flow control. When enabled, the DCE (modem) sends an XOFF to the DTE (computer) to halt data transmission to the DCE. When the DCE is ready to receive more data, an XON character is sent. XOUTFLOW typically uses the same 1/16th rules as XINPFLOW. Default = FALSE.

Common baud rates range from 110 to 256000. There are equates defined in the WIN32API.INC file, prefixed with %CBR_ to assist you with specifying a common baud rate, but you are not restricted to a limited set of rates.

PowerBASIC sets the ERR system variable if an error occurs when using the COMM function.

The Number symbol (#) prefix is optional, but recommended for the purposes of clarity.

Restrictions

Due to differences between Win32 operating systems, parameters (such as the TXBUFFER and TXQUE) may not be queried successfully in all circumstances.

See also

Serial Communications, COMM CLOSE, COMM LINE, COMM OPEN, COMM PRINT, COMM RECV, COMM RESET, COMM SEND, COMM SET

Example

Qty& = COMM(#hComm, RXQUE)

x$ = "The receive buffer contains " + _

FORMAT$(Qty&) + " bytes of data."

Qty& = COMM(#hComm, TXBUFFER) - COMM(#hComm, TXQUE)

x$ = "There is room for " + FORMAT$(Qty&) + _

" bytes in the transmit buffer."


COMM LINE statement

Purpose

Receive a CR/LF ($CRLF) terminated "line" of data from a serial port.

Syntax

COMM LINE [INPUT] [#] hComm, string_var

Remarks

Read a delimited line of data from the receive buffer, where a "line" is defined as a stream of data that is terminated by a CR/LF (carriage return and linefeed, $CRLF, or CHR$(13,10)). COMM LINE INPUT is ideal for retrieving modem response strings in reply to "AT" commands sent to a modem.

COMM LINE reads the receive buffer up to the next $CRLF character pair. The $CRLF bytes are removed from the buffer but do not form part of the string data returned by COMM LINE. Note that if there is no $CRLF pair in the receive buffer, the statement will wait indefinitely for a complete $CRLF terminated line of data. In this sense, COMM LINE is a blocking statement. The SetCommTimeouts API can be used to specify COMM timeouts.

The EOF function may also be used with COMM LINE (and TCP LINE) to detect that an incomplete line was received. Normally, the COMM LINE statement reads data until a $CRLF character pair is found, and in that case, EOF will return false (zero). However, if a timeout does occur, COMM LINE will return whatever data has been accumulated, and set EOF to logical TRUE (non-zero).

In many cases, it would be prudent to test EOF after every COMM LINE statement to verify that a full line has been received. In some cases, you may wish to execute the statement one or more additional times, combining the data, in order to obtain a full line of text.

The Number symbol (#) prefix is optional, but recommended for the purposes of clarity.

See also

Serial Communications, COMM CLOSE, COMM function, COMM OPEN, COMM PRINT, COMM RECV, COMM RESET, COMM SEND, COMM SET, EOF

Example

COMM PRINT #hComm, "AT"

SLEEP 1000 ' delay for modem to respond

DO

COMM LINE INPUT #hComm, a$

CALL DisplayResponse(a$) ' display the modem echo

LOOP UNTIL LEN(a$)


COMM OPEN statement

Purpose

Open a serial port.

Syntax

COMM OPEN "COMn" AS [#] hComm

Remarks

Opens a serial port to begin communications, creating a relationship between a file number and a specific serial port device.

COMn

Identifies the serial port number, for example, COM1, COM4, etc. A colon must not follow the port specification. See Restrictions below.

hComm

A valid variable containing a free PowerBASIC file number, typically provided by the FREEFILE function. Although not recommended, hComm can be specified as a numeric literal; however, using hard-coded values can be more difficult to manage successfully in large programs, and such code is unlikely to be thread-safe.

The Number symbol (#) prefix is optional, but recommended for the purposes of clarity.

If the port was not opened successfully, the ERR system variable will contain the error code. Before actual communications through the port can commence, you must configure the communication parameters by using a COMM SET statement for each parameter.

Restrictions

COMM OPEN cannot use an operating system file handle, nor open a port that is already in use. When opening ports above COM9, Windows requires the port name to be specified using the following syntax:

COMM OPEN "\\.\COM15" AS #hComm

See also

Serial Communications, COMM CLOSE, COMM function, COMM LINE, COMM PRINT, COMM RECV, COMM RESET, COMM SEND, COMM SET, FREEFILE, OPEN

Example

DIM hComm AS LONG

hComm = FREEFILE

COMM OPEN "COM1" AS #hComm

COMM OPEN "COM2" AS #5


COMM PRINT statement

Purpose

Send a "line" of binary data through a serial port with optional CR/LF ($CRLF) termination.

Syntax

COMM PRINT [#] hComm, string_expression [;]

Remarks

Places a copy of the data in string_expression into the transmit buffer to be sent to the serial port, followed by a carriage return and linefeed pair (CHR$(13,10) or $CRLF), unless a trailing semicolon is employed. This is a variation of COMM SEND and is intended as a convenience for transmitting text data.

The Number symbol (#) prefix is optional, but recommended for the purposes of clarity.

COMM PRINT is ideal for sending "AT" commands to a modem. Omit the trailing semicolon for this purpose, since you would want the CR/LF to be send along with the data.

Restrictions

Avoid using ASCIIZ strings to hold binary data, as any embedded CHR$(0) or $NUL characters will be misunderstood as indicating the end of the string.

See also

Serial Communications, COMM CLOSE, COMM function, COMM LINE, COMM OPEN, COMM RECV, COMM RESET, COMM SEND, COMM SET


COMM RECV statement

Purpose | Receive binary data from a serial port.
Syntax | COMM RECV [#] hComm, count&, string_var
Remarks | Retrieve the count& number of bytes from the receive buffer, placing the results in string_var. Program execution will halt until count& bytes are available, so it is wise to check how many bytes are available before making a COMM RECV request. You can do this by checking the RXQUE value with the COMM function, as shown in the example below. The Number symbol (#) prefix is optional, but recommended for the purposes of clarity.
Restrictions | Avoid using ASCIIZ string to hold binary data, as any embedded CHR$(0) or $NUL characters will be misunderstood as indicating the end of the string.
See also | Serial Communications, COMM CLOSE, COMM function, COMM LINE, COMM OPEN, COMM PRINT, COMM RESET, COMM SEND, COMM SET
Example | Qty& = COMM(#hComm, RXQUE) COMM RECV #hComm, Qty&, a$


COMM RESET statement

Purpose | Disable flow control for a given serial port.
Syntax | COMM RESET [#] hComm, FLOW
Remarks | Switches off all flow control to the serial port as specified by the file number stored in hComm. The Number symbol (#) prefix is optional, but recommended for the purposes of clarity.
See also | Serial Communications, COMM CLOSE, COMM function, COMM LINE, COMM OPEN, COMM PRINT, COMM RECV, COMM SEND, COMM SET


COMM SEND statement

Purpose

Send a string of binary data through a serial port.

Syntax

COMM SEND [#] hComm, string_expression

Remarks

Places a copy of the data in string_expression into the transmit buffer to be sent to the serial port. The data is appended to any data that may already be waiting in the transmit buffer. If string_expression is empty, no data is added to the transmit buffer.

The data is sent without trailing CR/LF ($CRLF or CHR$(13,10)) bytes appended to the data. If you are sending lines of text, you can either append CR ($CR or CHR$(13)) and/or LF ($LF or CHR$(10)) bytes to the text, or use the COMM PRINT statement instead.

The Number symbol (#) prefix is optional, but recommended for the purposes of clarity.

Restrictions

It is not always appropriate to use fixed length strings in string_expression, as the entire length of the string is added to the transmit buffer. Do not use ASCIIZ strings for binary data as any embedded CHR$(0) characters will be misunderstood as indicating the end of the string.

See also

Serial Communications, COMM CLOSE, COMM function, COMM LINE, COMM OPEN, COMM PRINT, COMM RECV, COMM RESET, COMM SET

Example

A$ = "ATDT1,555-1234;"

COMM SEND #hComm, a$


COMM SET statement

Purpose

Set communication options for a serial port.

Syntax

COMM SET [#] hComm, Comfunc = value

Remarks

Set the parameters needed to communicate with a serial port. This must always be done before you can send and receive data through the port.

To configure the communication parameters, use keywords from the following table to specify the Comfunc as well as a suitable value chosen from the range applicable to the Comfunc parameter you want to set. If an error occurs when attempting to set a parameter, PowerBASIC sets the ERR system variable to indicate the error number. While each parameter must be set individually, it is also possible to change certain parameters without the need to close and re-establish communications.

COMM SET keywords table

Comfunc

value (TRUE <> 0, FALSE = 0)

BAUD

Port Baud Rate (9600, 14400, 19200, etc). See notes below.

BREAK

TRUE/FALSE Break is asserted. Break is generally used to "get the attention" of the connected modem, terminal or system.

BYTE

Number of bits per byte (4, 5, 6, 7, or 8).

CD

TRUE/FALSE Carrier Detect state; synonym for RLSD (READ-ONLY). When CD is TRUE, the DCE (modem) has a suitable connection on the communications channel present. When CD is FALSE, there is no suitable connection.

CTSFLOW

TRUE/FALSE Enable CTS output flow control (Input signal). When CTSFLOW is enabled, it causes the DTE (computer) to stop sending data whenever the CTS signal is set to logic low by the DCE (modem). Transmission continues when the DCE (modem) sets the CTS signal back to logic high. The CTS signal is usually used in response to an RTS signal.

DSRFLOW

TRUE/FALSE Enable DSR output flow control (Input signal). When DSRFLOW is enabled, it causes the DTE (computer) to stop sending data whenever the DSR signal is set to logic low by the DCE (modem). Transmission is enabled when the DSR signal returns to logic high. The DSR signal is often used in conjunction with CTS in response to a RTS signal.

DSRSENS

TRUE/FALSE Enable DSR sensitivity. When DSRSENS is enabled, data received by the DTE (computer) is placed into the receive buffer only if DSR is set to logic high. If DSR is set low, received data is discarded. Enabling DSRSENS allows DSR to enable or disable the DTE (the computer) to receive data from the DTE (the modem). DSRSENS is rarely used in practical communications situations.

DTRFLOW

TRUE/FALSE Enable DTR handshaking flow control (Output signal). When DTRFLOW is enabled, it signals that the DCE (modem) should prepare to connect to the communications channel. DTR is usually used for modem on-hook/off-hook control, but can also be used in conjunction with DSR for handshaking.

DTRLINE

TRUE/FALSE Enable DTR line. When enabled, DTRLINE leaves the DTR line active when the port is closed by the DTE (computer). This ensures that the DCE (modem) does not close the communications channel when the port is closed.

NULL

TRUE/FALSE Null ($NUL) bytes are discarded when read.

PARITY

TRUE/FALSE Enable parity checking. This mode must be enabled for the other Parity options to be selected.

PARITYCHAR

Character to use for parity error replacement. PARITY must be enabled.

PARITYREPL

TRUE/FALSE Enable character replacement on parity error. PARITY must be enabled.

PARITYTYPE

0 = None, 1 = Odd, 2 = Even, 3 = Mark, 4 = Space. PARITY must be enabled. Default = 0.

RING

TRUE/FALSE Ring indicator is on (READ-ONLY). When RING returns TRUE, a ringing signal is being received on the communications channel (by the modem). RING approximates the state of the ringing signal; however, it may not be reported accurately on all Windows platforms.

RLSD

Receive-line-signal-detect (READ-ONLY). See CD/Carrier Detect above.

RTSFLOW

Ready To Send (Output signal). 0 = Disable, 1 = Enable, 2 = Handshake, 3 = Toggle. Toggle is used for half-duplex (2-wire) operations to "reverse" the line. While the DTE (computer) is busy sending data, it raises the RTS signal and the DCE (modem) blocks its data receive channel. When RTS signal reverts to logic low, the DCE (modem) reverts to transmit mode and the DTE (computer) switches to receive mode.

Handshake mode causes the DTE (computer) to check the receive buffer (RXQUE) after each character is placed into the buffer. When the buffer is 5/6th full, the RTS signal is dropped. When the receive buffer drops to below 1/6th full, RTS is raised again.

RXBUFFER

Size of the receive buffer in bytes.

RXQUE

Bytes currently in the receive buffer (READ-ONLY).

STOP

0 = 1 stop bits, 1 = 1.5 stop bits, 2 = 2 stop bits.

TXBUFFER

Size of the transmit buffer in bytes. In some cases, Windows may not be able to report the transmit size.

TXQUE

Bytes currently in the transmit buffer (READ-ONLY).

XINPFLOW

TRUE/FALSE Enable XON/XOFF input flow control. When the DTE (computer) receive buffer is full, an XOFF character is sent to the DCE (modem) to instruct it to halt transmission. When the DCE is ready to resume transmission, an XON character is sent to the DCE. Typically, XOFF is sent when the receive buffer has less than 1/16th remaining, and XON is sent when the receive buffer drops to less than 1/16th of its maximum size. Default = FALSE.

XOUTFLOW

TRUE/FALSE Enable XON/XOFF out flow control. When enabled, the DCE (modem) sends an XOFF to the DTE (computer) to halt data transmission to the DCE. When the DCE is ready to receive more data, an XON character is sent. XOUTFLOW typically uses the same 1/16th rules as XINPFLOW. Default = FALSE.

Common baud rates range from 110 to 256000. There are equates defined in the WIN32API.INC file, prefixed with %CBR_ to assist you with specifying a common baud rate, but you are not restricted to a limited set of rates.

Attempting to set a READ-ONLY attribute will result in a compile-time Error 542 ("May not be altered").

The Number symbol (#) prefix is optional, but recommended for the purposes of clarity.

See also

Serial Communications, COMM CLOSE, COMM function, COMM LINE, COMM OPEN, COMM PRINT, COMM RECV, COMM RESET, COMM SEND

Example

To open a communication port and initialize it for use, you will need to set the following parameters (the selection is typical, but is mainly for demonstration purposes - you may choose your own settings as necessary)

' Minimum settings

COMM SET #hComm, BAUD = 9600 ' 9600 baud

COMM SET #hComm, BYTE = 8 ' 8 bits

COMM SET #hComm, PARITY = %FALSE ' No parity

COMM SET #hComm, STOP = 0 ' 1 stop bit

COMM SET #hComm, TXBUFFER = 2048 ' transmit buffer

COMM SET #hComm, RXBUFFER = 4096 ' receive buffer

' Optional settings for flow control

COMM SET #hComm, CTSFLOW = 1 ' Enable CTS

COMM SET #hComm, RTSFLOW = 1 ' Enable RTS

COMM SET #hComm, XINPFLOW = 0 ' Disable XON/OFF

' Input flow control

COMM SET #hComm, XOUTFLOW = 0 ' Disable XON/XOFF

' Output flow control


COMMAND$ function

Purpose

Return the command-line used to start the program.

Syntax

s$ = COMMAND$

Remarks

COMMAND$ returns everything that was typed following the program name. Some operating system manuals refer to this text as the trailer or command tail.

Use COMMAND$ to collect run-time arguments, like filenames, and program options.

For example, consider a program named FASTSORT.EXE that reads data from one file, sorts it, and puts the result in a new file. Using COMMAND$ lets you specify the input and output file names when the program is invoked:

FASTSORT.EXE cust.dta cust.new

When FASTSORT begins execution, COMMAND$ will hold the :

cust.dta cust.new

FASTSORT must include code to parse this string into two file names.

Restrictions

In some recent versions of Windows, file association and drag-drop file operations cause filenames to be enclosed with double-quote marks when they are passed in COMMAND$. It would be wise to ensure that your applications are prepared for this possibility. Some operating systems automatically enclose the command-line in double-quote marks.

PowerBASIC imposes no arbitrary limits on the length of the string returned by COMMAND$ but, the operating system may impose limits. Such limits may become evident, for example, when attempting to Drag and Drop a large number of files onto an EXE within Windows Explorer. Usually, attempting to drop more files than the operating system permits will result in an operating system warning message.

Within the IDE, a COMMAND$ command-line parameter can be specified for the purposes of testing in both Compile and Execute and Compile and Debug modes.

See also

JOIN$, PARSE, PARSE$, PARSECOUNT, WINMAIN

Example

#COMPILE EXE

FUNCTION PBMAIN

IF TRIM$(COMMAND$) = "" THEN

EXIT FUNCTION ' No command-line params given, just quit

ELSEIF INSTR(COMMAND$, "/Q") THEN

' Process the /Q option

ELSEIF INSTR(COMMAND$, "/W") THEN

' Process the /W option

END IF

END FUNCTION


CONSHNDL function

Purpose | Return the window handle of the console window.
Syntax | hConsole??? = CONSHNDL
Remarks | The console window handle can be used with some Win32 API calls, and can assist when associating GUI windows with the console window.
See also | CONSOUT, GETSTDERR, GETSTDIN, GETSTDOUT, STDEOF, STDERR, STDIN LINE, STDOUT


CONSIN function

Purpose | Determine if standard input is coming from the console input buffer, or has been redirected from another source.
Syntax | v& = CONSIN
Remarks | CONSIN returns TRUE (non-zero) if standard input is originating at the console keyboard, or FALSE (zero) if it is redirected from a file or device, which is determined at the start of program execution.
See also | CONSHNDL, CONSOUT, GETSTDERR, GETSTDIN, GETSTDOUT, STDEOF, STDERR, STDIN LINE, STDOUT
Example | IF ISTRUE CONSIN THEN A$ = "STDIN is taking input from the keyboard" ELSE A$ = "STDIN has been redirected" END IF


CONSNAME$ function

Purpose | Retrieve console window caption.
Syntax | string_var = CONSNAME$
Remarks | string_var receives the text of the console window caption. This area is also referred to as the "title bar" or the "window caption".
See also | CONSHNDL, CONSOLE NAME, SLEEP
Example | OrigTitle$ = CONSNAME$ ' Store the console title


CONSOLE GET LOC statement

Purpose | Retrieve the location of the console window on the screen.
Syntax | CONSOLE GET LOC TO x&, y&
Remarks | The location is a pixel coordinate relative to the upper left corner of the screen. This statement can be especially useful in conjunction with CONSOLE GET SIZE and GRAPHIC WINDOW, for exact positioning of graphic windows with respect to the console window.
See also | CONSOLE GET SCREEN, CONSOLE GET SIZE, CONSOLE GET VIEW, CONSOLE GET VIRTUAL, CONSOLE SET LOC, CONSOLE SET SCREEN, CONSOLE SET VIEW, CONSOLE SET VIRTUAL
Example | FUNCTION PBMAIN () AS LONG ' Center a graphic window within a console window LOCAL x, y, w, h AS LONG, hWin AS DWORD CONSOLE GET LOC TO x, y ' Console pos on screen CONSOLE GET SIZE TO w, h ' Console size x = x + (w - 200) / 2 ' Calculate center x pos y = y + (h - 200) / 2 ' Calculate center y pos GRAPHIC WINDOW "Box", x, y, 200, 200 TO hWin GRAPHIC ATTACH hWin, 0 GRAPHIC BOX (10, 10) - (190, 190), 0, %RED SLEEP 5000 ' show it for 5 seconds, then end END FUNCTION


CONSOLE GET SCREEN statement

Purpose | Retrieve the size of the visible part of the console, in text rows and columns.
Syntax | CONSOLE GET SCREEN TO ncRows&, ncColumns&
Remarks | Most PowerBASIC statements specify graphic and pixel positions as x, y (the horizontal term first, then the vertical term). However, for compatibility with most current and prior versions of BASIC (PowerBASIC included), the statements which reference console rows and columns name the vertical term first (rows, columns). You can change the size of the console with CONSOLE SET SCREEN.
See also | CONSOLE GET LOC, CONSOLE GET SIZE, CONSOLE GET VIEW, CONSOLE GET VIRTUAL, CONSOLE SET LOC, CONSOLE SET SCREEN, CONSOLE SET VIEW, CONSOLE SET VIRTUAL


CONSOLE GET SIZE statement

Purpose | Retrieve the size of the console window, in pixels.
Syntax | CONSOLE GET SIZE TO nWidth&, nHeight&
Remarks | This statement can be especially useful together with CONSOLE GET LOC and GRAPHIC WINDOW, for exact positioning of graphic windows relative to the position and size of the console window.
See also | CONSOLE GET LOC, CONSOLE GET SCREEN, CONSOLE GET VIEW, CONSOLE GET VIRTUAL, CONSOLE SET LOC, CONSOLE SET SCREEN, CONSOLE SET VIEW, CONSOLE SET VIRTUAL
Example | FUNCTION PBMAIN () AS LONG ' Center a graphic window within a console window LOCAL x, y, w, h AS LONG, hWin AS DWORD CONSOLE GET LOC TO x, y ' Console pos on screen CONSOLE GET SIZE TO w, h ' Console size x = x + (w - 200) / 2 ' Calculate center x pos y = y + (h - 200) / 2 ' Calculate center y pos GRAPHIC WINDOW "Box", x, y, 200, 200 TO hWin GRAPHIC ATTACH hWin, 0 GRAPHIC BOX (10, 10) - (190, 190), 0, %RED SLEEP 5000 ' show it for 5 seconds, then end END FUNCTION


CONSOLE GET VIEW statement

Purpose

Retrieve the position of the viewable part of a virtual screen.

Syntax

CONSOLE GET VIEW TO nRow&, nColumn&

Remarks

PowerBASIC allows you to specify a virtual console screen that may, optionally, be larger than the visible part of the console. If you create a virtual screen with CONSOLE SET VIRTUAL, scroll bars are automatically added to the visible screen to allow the user to examine the entire screen buffer.

If you create a virtual screen that is larger than the visible screen, PowerBASIC initially positions the viewable part at the upper left corner (position 1,1). The viewable part can be moved to various parts of the virtual screen by the user (with console scroll bars), or by the programmer (with the CONSOLE SET VIEW statement).

CONSOLE GET VIEW retrieves the view position of the visible page, regardless of whether it is also the active page.

Most PowerBASIC statements specify graphic and pixel positions as x,y (the horizontal term first, then the vertical term). However, for compatibility with most current and prior versions of BASIC (PowerBASIC included), the statements which reference console rows and columns name the vertical term first (rows, columns).

See also

CONSOLE GET LOC, CONSOLE GET SCREEN, CONSOLE GET SIZE, CONSOLE GET VIRTUAL, CONSOLE SET LOC, CONSOLE SET SCREEN, CONSOLE SET VIEW, CONSOLE SET VIRTUAL


CONSOLE GET VIRTUAL statement

Purpose

Retrieve the size of the screen buffers associated with the console window.

Syntax

CONSOLE GET VIRTUAL TO ncRows&, ncColumns&

Remarks

PowerBASIC allows you to specify a virtual console screen that may, optionally, be larger than the visible screen. If you create a virtual screen with CONSOLE SET VIRTUAL, scroll bars are automatically added to the visible screen to allow the user to examine the entire screen buffer.

The CONSOLE GET VIRTUAL statement retrieves the virtual screen size. You can change the size of the console buffer with CONSOLE SET VIRTUAL.

Most PowerBASIC statements specify graphic and pixel positions as x,y (the horizontal term first, then the vertical term). However, for compatibility with most current and prior versions of BASIC (PowerBASIC included), the statements which reference console rows and columns name the vertical term first (rows, columns).

See also

CONSOLE GET LOC, CONSOLE GET SCREEN, CONSOLE GET SIZE, CONSOLE GET VIEW, CONSOLE SET LOC, CONSOLE SET SCREEN, CONSOLE SET VIEW, CONSOLE SET VIRTUAL


CONSOLE NAME statement

Purpose | Set the console window caption.
Syntax | CONSOLE NAME string_expression
Remarks | The text displayed in the window caption is changed to the contents of string_expression.
See also | CONSHNDL, CONSNAME$, SLEEP
Example | CONSOLE NAME "My PB/CC Program"


CONSOLE SCREEN statement

Purpose

Change the size of the console window.

Syntax

CONSOLE SCREEN ncRows&, ncColumns&

Remarks

The size of the console window is changed to ncRows& rows and ncColumns& columns. When the console is resized, all text pages are resized to the new value.

The CONSOLE SCREEN statement is obsolete and will be removed in a future version of PowerBASIC. Use CONSOLE SET SCREEN instead.

See also

CONSOLE SET SCREEN


CONSOLE SET FOCUS statement

Purpose | Bring the console window to the foreground and direct the focus to it.
Syntax | CONSOLE SET FOCUS
Remarks | This statement brings the console window to the foreground, directing focus to it, so the program can receive keyboard input. It is particularly useful after creating or referencing a graphic window.
See also | GRAPHIC WINDOW, GRAPHIC SET FOCUS


CONSOLE SET LOC statement

Purpose | Move the console window to a new location on the screen.
Syntax | CONSOLE SET LOC x&, y&
Remarks | The location is a pixel coordinate relative to the upper left corner of the screen.
See also | CONSOLE GET LOC, CONSOLE GET SCREEN, CONSOLE GET SIZE, CONSOLE GET VIEW, CONSOLE GET VIRTUAL, CONSOLE SET SCREEN, CONSOLE SET VIEW, CONSOLE SET VIRTUAL, DESKTOP GET CLIENT, DESKTOP GET LOC, DESKTOP GET SIZE


CONSOLE SET SCREEN statement

Purpose

Change the size of the console window and its associated screen buffers.

Syntax

CONSOLE SET SCREEN ncRows&, ncColumns&

Remarks

The size of the console window (and the buffer for each active page) is changed to the rows and columns specified. It should be noted that, when using Windows 95/98/ME, a change in the number of rows to a non-standard value (other than 25, 43, 60) will substantially increase the execution speed of PRINT statements.

Once the console size has been altered, the new size can be retrieved with the CONSOLE GET SCREEN statement.

If CONSOLE SET SCREEN did not succeed, error 5 (Illegal Function Call) is generated. This is most likely to be caused by one or more parameters that are too large or too small for the operating system in use.

If a non-standard screen size is used, switching the console to full screen mode may cause the console to be resized by Windows to the closest standard size. The precise course of action taken by Windows in this case is version-dependent.

The default console size depends on the Windows version and on the user’s local settings.

Previous versions of PowerBASIC allowed a syntax of CONSOLE SCREEN (without the SET directive, which was added for syntax symmetry, and ease of remembering it). While the old syntax will be supported for some period of time, we urge you to change to the new syntax as soon as possible, to avoid future compatibility issues.

Most PowerBASIC statements specify graphic and pixel positions as x, y (the horizontal term first, then the vertical term). However, for compatibility with most current and prior versions of BASIC (PowerBASIC included), the statements which reference console rows and columns name the vertical term first (rows, columns).

Restrictions

This statement was designed for use with the WinNT family of operating systems (WinNT, Win2000, WinXP). It may not be possible to use all the features under Win95/98/ME, due to faults in the Windows subsystems. At a minimum, under Win95/98/ME, you must disable FONT AUTO-SIZE, and choose a specific fixed font for the console. Then test the features you wish to use, to verify accurate execution.

If a non-standard console size is in use, switching the console to full-screen mode may cause the console to be resized by Windows to the closest standard size. The exact result will depend on factors such as the version of Windows, fonts, and graphics hardware and drivers installed.

to DOS or console applications that have not been written to specifically handle non-standard console sizes can result in unpredictable application behavior and crashes. When in doubt, restore the console to a standard size before using SHELL, then resize it back afterward.

See also

CONSOLE GET LOC, CONSOLE GET SCREEN, CONSOLE GET SIZE, CONSOLE GET VIEW, CONSOLE GET VIRTUAL, CONSOLE SET LOC, CONSOLE SET VIEW, CONSOLE SET VIRTUAL, DESKTOP GET CLIENT, DESKTOP GET LOC, DESKTOP GET SIZE


CONSOLE SET VIEW statement

Purpose

Set the position of the viewable part of a virtual screen.

Syntax

CONSOLE SET VIEW nRow&, nColumn&

Remarks

PowerBASIC allows you to specify a virtual console screen that may, optionally, be larger than the visible console. If you create a virtual screen with CONSOLE SET VIRTUAL, scroll bars are automatically added to the visible screen to allow the user to examine the entire screen buffer.

If you create a virtual screen that is larger than the visible screen, PowerBASIC initially positions the viewable part at the upper left corner (position 1,1). You may change the upper left corner of the view port within the virtual screen with CONSOLE SET VIEW. If you specify a position which is outside the limits of the virtual screen, PowerBASIC will select a location at the maximum limit in that direction.

The CONSOLE SET VIEW statement affects only the visible console page, regardless of whether it is also the active page. You can retrieve the current VIEW position with the CONSOLE GET VIEW statement.

Most PowerBASIC statements specify graphic and pixel positions as x ,y (the horizontal term first, then the vertical term). However, for compatibility with most current and prior versions of BASIC (PowerBASIC included), the statements which reference console rows and columns name the vertical term first (rows, columns).

Restrictions

This statement was designed for use with the WinNT family of operating systems (WinNT, Win2000, WinXP). It may not be possible to use all the features under Win95/98/ME, due to faults in the Windows subsystems. At a minimum, under Win95/98/ME, you must disable FONT AUTO-SIZE, and choose a specific fixed font for the console. Then test the features you wish to use, to verify accurate execution.

See also

CONSOLE GET LOC, CONSOLE GET SCREEN, CONSOLE GET SIZE, CONSOLE GET VIEW, CONSOLE GET VIRTUAL, CONSOLE SET LOC, CONSOLE SET SCREEN, CONSOLE SET VIRTUAL, DESKTOP GET CLIENT, DESKTOP GET LOC, DESKTOP GET SIZE


CONSOLE SET VIRTUAL statement

Purpose

Change the size of the buffers associated with the console window.

Syntax

CONSOLE SET VIRTUAL ncRows&, ncColumns&

Remarks

PowerBASIC allows you to specify a virtual console screen that may, optionally, be larger than the visible console. If you create a virtual screen with CONSOLE SET VIRTUAL, scroll bars are automatically added to the visible screen to allow the user to examine the entire screen buffer.

When you execute CONSOLE SET VIRTUAL, the size of the console screen buffer for each active page is changed to the rows and columns specified. Each dimension must be larger or equal to the corresponding dimension of the current CONSOLE SET SCREEN size.

Once the console buffer size has been altered, the new size can be retrieved using the CONSOLE GET VIRTUAL statement.

If CONSOLE SET VIRTUAL cannot be successfully executed, an error 5 (Illegal Function Call) is generated. This would most likely be caused by one or more parameters which are too large for available memory, or smaller than the visible console screen size.

Most PowerBASIC statements specify graphic and pixel positions as x, y (the horizontal term first, then the vertical term). However, for compatibility with most current and prior versions of BASIC (PowerBASIC included), the statements which reference console rows and columns name the vertical term first (rows, columns).

Restrictions

This statement was designed for use with the WinNT family of operating systems (WinNT, Win2000, WinXP). It may not be possible to use all the features under Win95/98/ME, due to faults in the Windows subsystems. At a minimum, under Win95/98/ME, you must disable FONT AUTO-SIZE, and choose a specific fixed font for the console. Then test the features you wish to use, to verify accurate execution.

See also

CONSOLE GET LOC, CONSOLE GET SCREEN, CONSOLE GET SIZE, CONSOLE GET VIEW, CONSOLE GET VIRTUAL, CONSOLE SET LOC, CONSOLE SET SCREEN, CONSOLE SET VIEW, DESKTOP GET CLIENT, DESKTOP GET LOC, DESKTOP GET SIZE


CONSOUT function

Purpose | Determine if standard output is sent to the console window, or has been redirected to a file or device.
Syntax | v& = CONSOUT
Remarks | v& is the result of the console redirection test, and is determined at the start of program execution. CONSOUT returns logical TRUE (non-zero) if standard output is displayed on the console screen, or FALSE (zero) if it is redirected to a file or device.
See also | CONSHNDL, CONSIN, GETSTDERR, GETSTDIN, GETSTDOUT, STDEOF, STDERR, STDIN LINE, STDOUT
Example | IF ISTRUE(CONSOUT) THEN PRINT "STDOUT is sending output to the display" ELSE PRINT "STDOUT has been redirected" END IF


COS function

Purpose | Return the cosine of an angle.
Syntax | y = COS(numeric_expression)
Remarks | numeric_expression is an angle specified in radians. To convert radians to degrees, multiply by 57.29577951308232##. To convert degrees to radians, multiply by 0.0174532925199433##. For more information on radians, see the ATN function. COS returns an Extended-precision value that always ranges between -1 and +1 inclusive. The Inverse Cosine (ARCCOS) of a value can be calculated as follows: pi# = 3.141592653589793## ArcCos = pi# / 2 - ATN(Value / SQR(1 - Value * Value)) The Hyperbolic Cosine (COSH) of a value can also be calculated: CosH = (EXP(Value) + EXP(-Value)) / 2 The Inverse Hyperbolic Cosine (ARCCOSH) of a value can also be calculated: ArcCosH = LOG(Value + SQR(Value * Value - 1)) ' Useful Macro functions MACRO Pi = 3.141592653589793## MACRO DegreesToRadians(dpDegrees) = (dpDegrees*0.0174532925199433##) MACRO RadiansToDegrees(dpRadians) = (dpRadians*57.29577951308232##)
See also | ATN, SIN, TAN
Example | pi## = 3.141592653589793## ' we could also use pi## = ATN(1) * 4 FOR I& = 0 TO 360 STEP 45 x$ = "Cosine of " + FORMAT$(I&, "* 0") + _ " degrees = " + FORMAT$(COS(pi## / 180 * _ I&), "* 0.00") NEXT I&
Result | Cosine of 0 degrees = 1.00 Cosine of 45 degrees = 0.71 Cosine of 90 degrees = 0.00 Cosine of 135 degrees = -0.71 Cosine of 180 degrees = -1.00 Cosine of 225 degrees = -0.71 Cosine of 270 degrees = 0.00 Cosine of 315 degrees = 0.71 Cosine of 360 degrees = 1.00


CSET$ function

Purpose

Return a containing a centered (padded) string.

Syntax

a$ = CSET$(string_expression, strlen& [USING ustring_expression])

Remarks

CSET$ centers the string string_expression into a string of strlen& characters.

USING

If ustring_expression is null (empty) or is not specified, CSET$ pads string_expression with space characters. Otherwise, CSET$ pads the string with the first character of ustring_expression.

If string_expression is shorter then strlen&, CSET$ centers string_expression within result_var, padding both sides as described above; otherwise, CSET$ returns the left-most strlen& bytes of string_expression.

See also

CSET, GET, LET, LSET, LSET$, PUT, RESET, RSET, RSET$, STRINSERT$, TYPE SET

Example

a$ = CSET$("PowerBASIC", 20)

' result: " PowerBASIC "

a$ = CSET$("PowerBASIC",20 USING "*")

' result: "*****PowerBASIC*****"


CSET statement

Purpose

Center a within the space of another string or User-Defined Type.

Syntax

CSET [ABS] result_var = string_expression [USING string_expression]

Remarks

CSET centers a string into the space of another string or variable of a User-Defined Type.

ABS

If ABS is specified, or ustring_expression is null (empty), CSET leaves the padding positions unchanged from their original content, rather than replacing them with spaces.

USING

If string_expression is shorter then result_var, CSET centers string_expression within result_var, padding both sides with the first character in ustring_expression, or spaces if not specified.

If string_expression is longer than result_var, CSET truncates string_expression from the right until it fits in result_var.

CSET can be used to assign the content of a User-Defined Type to a User-Defined Type variable of a different class, or assign a dynamic string to a User-Defined Type. For example:

CSET MyType = MyType2

CSET MyType = a$

LSET and RSET work similarly, but performs left and right-justification respectively.

See also

CSET$, GET, LET, LSET, LSET$, PUT, RESET, RSET, RSET$, STRINSERT$, TYPE SET

Example

a$ = RTRIM$(REPEAT$(5,"COOL "))

CSET ABS a$ = "..PowerBASIC.."

' result: "COOL ..PowerBASIC.. COOL"

CSET a$ = "PowerBASIC" USING "*"

' result: "*******PowerBASIC*******"


CURDIR$ function

Purpose

Return the current directory path for the specified drive.

Syntax

s$ = CURDIR$[(drive$)]

Remarks

drive$ is an optional string expression, containing the drive letter of the target disk drive. If drive$ is not specified or is an empty , the current directory path is returned for the default drive.

See also

CHDRIVE, CHDIR

Example

FUNCTION PBMAIN

LOCAL FullCurrentPath$

LOCAL CurrentDrive$

FullCurrentPath$ = CURDIR$

IF MID$(CURDIR$,2,1) = ":" THEN

CurrentDrive$ = LEFT$(CURDIR$,2)

END IF

END FUNCTION


CURSOR function

Purpose | Return the status of the caret.
Syntax | intvar = CURSOR
Remarks | Returns the size of the caret. A size of zero (0) indicates that the caret has been turned off. Otherwise, it is a percentage of the total caret size (100 = full sized caret).
See also | CURSOR statement, CURSORX, CURSORY, LOCATE, PAGE, PAGEACTIVE, PAGEVISIBLE, PRINT


CURSOR statement

Purpose | Make the caret visible or invisible, or change the caret size.
Syntax | CURSOR {OFF | ON [,intvar]}
Remarks | An optional numeric intvar parameter in the range of 1 to 100 (representing the percentage of the height of a full caret) may be added to the ON option.
See also | CURSOR function, CURSORX, CURSORY, LOCATE, PAGE, PAGEACTIVE, PAGEVISIBLE, PRINT
Example | CURSOR ON, 100 ' turn the caret on, full-size


CURSORX function

Purpose | Return the current column position of the caret.
Syntax | nColumn& = CURSORX
Remarks | The return value will be in the range of 1 to the maximum number of columns in the console, which may be retrieved with the SCREENX function. CURSORX is functionally equivalent to the POS function in DOS BASIC. The console size may be changed using CONSOLE SCREEN.
See also | CURSOR function, CURSOR statement, CURSORY, LOCATE, SCREENX


CURSORY function

Purpose | Return the current row position of the caret.
Syntax | nRow& = CURSORY
Remarks | The return value will be from 1 to the maximum number of rows in the console, which may be retrieved with the SCREENY function. CURSORY is functionally equivalent to the CSRLIN function in DOS BASIC. The console size may be changed using CONSOLE SCREEN.
See also | CURSOR function, CURSOR statement, CURSORX, LOCATE, SCREENY


CVBYT functions

CVBYT, CVCUR, CVCUX, CVD, CVDWD, CVE, CVI, CVL, CVQ, CVS and CVWRD functions

Purpose

Convert data to form.

Syntax

bytevar? = CVBYT(variable [, offset])

curvar@ = CVCUR(variable [, offset])

cuxvar@@ = CVCUX(variable [, offset])

doublevar# = CVD (variable [, offset])

doublewordvar??? = CVDWD(variable [, offset])

extendedvar## = CVE (variable [, offset])

integervar% = CVI (variable [, offset])

longintvar& = CVL (variable [, offset])

quadintvar&& = CVQ (variable [, offset])

singlevar! = CVS (variable [, offset])

wordvar?? = CVWRD(variable [, offset])

Remarks

The CVx functions return a number corresponding to a binary pattern stored in a string value. The MKx functions are complementary to the CVx functions. Do not confuse these functions with the VAL function, which converts a number stored as a printable text string (such as "123.45") into a numeric expression.

The CVx functions allow retrieving values beyond the first byte of the input string variable, where offset indicates at what position in the string the function should retrieve value. variable may be either a User-Defined Type or a string.

For example: "Value& = CVL(x$, 3)" would extract the 3rd through 6th bytes of x$ and convert these 4 bytes to the corresponding Long-integer value. In this example, x$ must be at least 6 bytes long.

Function | Variable | Converts to
CVBYT | 1-byte string | Byte
CVCUR | 8-byte string | Currency
CVCUX | 8-byte string | Extended-currency
CVD | 8-byte string | Double-precision float
CVDWD | 4-byte string | Double-word
CVE | 10-byte string | Extended-precision float
CVI | 2-byte string | Integer
CVL | 4-byte string | Long-integer
CVQ | 8-byte string | Quad-integer
CVS | 4-byte string | Single-precision float
CVWRD | 2-byte string | Word

Expressions involving Numeric Equates and conditional compilation (#IF) may also include the CVQ function. This allows you to easily assign numeric values to an equate, based upon a meaningful mnemonic. In this context, the CVQ expression is limited to a length of eight bytes. For example:

%Mode = CVQ("DEBUG")

%Style = CVQ("Cool")

See also

MKI$ and associated functions


DATA statement

Purpose

Declare string constants within the source code to be read by READ$ function.

Syntax

DATA ["]item["] [[, ["]item["]] ...]

Remarks

DATA statements may only appear inside of Subs or Functions, and are visible only to the code in the Sub/Function in which they appear. Each procedure may therefore have its own private data.

Data may consist of virtually any text characters. Data items may be enclosed in quotes to preserve leading/trailing spaces, which are otherwise stripped during compilation.

Restrictions

There is a limit of 64 Kilobytes and 16384 separate data items per Sub or Function. Previous versions of PowerBASIC ignored plain text located immediately after a quoted literal up to the next comma or end-of-line; however, this is no longer acceptable and generates an Error 477 ("Syntax error").

DATA statements cannot extend across more than one physical source code line using line continuation characters. Special care should be used when formatting DATA statements, especially if the data is to contain underscore and/or colon characters. The following examples highlight data items in blue:

If an underscore appears after a comma, it is treated as the start of a quoted data string, rather than a line continuation character:

' Three data items exist in this line:

DATA "Tom", "Dick", _Harry

The colon (statement separator) character, when used within unquoted string data, performs as a regular statement separator:

' Two data items and a separate statement

DATA "Tom","Dick" : Harry& = 1

However, if a colon character appears within a quoted data string, it is treated as part of the data string:

' 3 data items

DATA "Tom",Dick,":Harry& = 1"

See also

DATACOUNT, READ$, VAL

Example

DATA "Abc", Bob, "Sally", 123

DATA 456.78, " leading space"

DATA embedded "quotes within data"


DATACOUNT function

Purpose | Return the total count of the number of local DATA items that can be read with the READ$ function.
Syntax | Count% = DATACOUNT
Remarks | DATACOUNT only returns the number of DATA items in the Sub or Function in which it appears (i.e., local DATA statements). While it is not possible to directly read data from outside of the scope of current Sub or Function, global data can be emulated easily by placing it inside a Function that returns data to the calling code. There is a limit of 64 Kilobytes and 16384 separate data items per Sub or Function.
See also | DATA, READ$
Example | FUNCTION GetCategories(Category() AS STRING) AS LONG LOCAL x AS INTEGER REDIM Category(1 TO DATACOUNT) AS STRING FOR x = 1 TO DATACOUNT Category(x) = READ$(x) NEXT x FUNCTION = DATACOUNT DATA Animal, Mineral, Vegetable, Alien END FUNCTION


DATE$ system variable

Purpose

Set or retrieve the system date.

Syntax

DATE$ = s$ ' sets system date according to s$

s$ = DATE$ ' s$ now contains system date

Remarks

Assigning a properly formatted value to DATE$ sets the system date. You can also assign DATE$ to a string variable, which stores 10 characters in the form "mm-dd-yyyy", where mm represents the month, dd the day, and yyyy the year.

To change the date, your date string must be formatted in one of the following ways:

mm-dd-yy

mm/dd/yy

mm-dd-yyyy

mm/dd/yyyy

For example, DATE$ = "11-09-84" sets the system date to November 9, 1984.

Restrictions

The year assigned to the DATE$ system variable must be within the range 1980 to 2099. DATE$ never returns locale-specific date formats.

See also

TIME$


DECLARE statement

Purpose

Explicitly declare a Sub or Function

Syntax

DECLARE {SUB | FUNCTION} ProcName [BDECL | CDECL | SDECL] [LIB

"LibName"] [ALIAS "AliasName"] [([arguments])] [AS type]

Remarks

The DECLARE statement has the following parts:

ProcName

The name of the Sub or Function to be declared. For Functions, a type-specifier may be appended (just like an ordinary variable name) to specify the of the Function's return value, in place of the [AS type] clause.

Future versions of PowerBASIC may not support type-specifier symbols for the Function return type, so specify the return data type with an explicit AS type clause in all DECLARE and FUNCTION definitions, to ensure future compatibility.

BDECL

Specifies that the declared procedure uses the BASIC/Pascal calling convention. When a procedure calls a BDECL procedure, it passes its parameters on the stack from left to right.

It is the responsibility of the called procedure to clean up the stack before returning to the calling procedure. Therefore, all PowerBASIC Subs and Functions that specify the BDECL convention automatically clean up the stack before execution returns to the calling code.

CDECL

Specifies that the declared procedure uses the C calling convention. When a procedure calls a CDECL procedure, it passes its parameters on the stack from right to left.

In addition, the calling procedure must remove any passed parameters from the stack as part of the return process. When PowerBASIC code calls Subs and Functions using the CDECL convention, the stack is cleaned automatically after execution returns from the called code.

In the event the called procedure is imported or exported, PowerBASIC will automatically create a lowercase ALIAS, prefixed with an underscore. Any periods in the name are replaced with underscores at the same time.

CDECL may be used for declaring external procedures written in the C language, or another language that follows C calling and parameter-passing conventions. The following two declarations are equivalent, indicating how the default ALIAS name would be created by PowerBASIC:

DECLARE SUB C_Function CDECL ()

DECLARE SUB C_Function CDECL ALIAS "_c_function" ()

SDECL

This is the default if neither BDECL nor CDECL are specified. SDECL (and its synonym STDCALL), specifies that the declared procedure uses the "Standard Calling Convention" as defined by Microsoft. When calling an SDECL procedure, parameters are passed on the stack from right to left.

PowerBASIC Subs and Functions that use the SDECL/STDCALL convention automatically clean up the stack before execution returns to the calling code.

LIB

A string literal or equate that specifies the name of the module in which a procedure or function is located. This allows you to call Subs or Functions that reside in DLLs. Unlike 16-bit Windows, you must include the .DLL extension in the name of the DLL you wish to access.

DECLARE SUB MySub LIB "ZUSER32.DLL" ()

ALIAS

A string literal that identifies the name and capitalization of the procedure in the external DLL. This lets you call a procedure or function by a name other than what it was originally named. This is useful if you want to abbreviate a long name, or if the original name of a function contains characters that are illegal in PowerBASIC. The AliasName is the routine's actual name, and Name is the title that you can use in PowerBASIC. For example:

DECLARE SUB ShortName ALIAS "VeryLongProcName"()

DECLARE FUNCTION LegalName ALIAS "Illegal$Name"()

Although a Name must be unique, you may use the same AliasName in multiple declarations. This is particularly useful for avoiding AS ANY in cases where a procedure is designed to receive several different types of parameters.

DECLARE FUNCTION AddAtom LIB "KERNEL32.DLL" ALIAS "AddAtomA"

(lpString AS ASCIIZ) AS WORD

DECLARE FUNCTION AddIntAtom LIB "KERNEL32.DLL" ALIAS "AddAtomA"

(BYVAL lpString AS DWORD) AS WORD

The ALIAS clause is very important when exporting Subs and Functions from DLLs. Omitting the ALIAS clause or incorrectly capitalizing the alias name are common causes of DLL load failure problems. Please refer to the SUB and FUNCTION sections for more information.

Passing parameters

arguments

Contains the name(s) or the type of each parameter, in the order they are passed, for up to 32 parameters. The use of the DECLARE statement in your main program is required for all procedures and functions which your code accesses, if they are located in DLLs called by your code. They can also be used to allow forward references to subs and functions.

The complete arguments list must be specified for each routine. Each parameter may be defined in one of three ways:

  • List only its type name (INTEGER, DOUBLE, etc.)

  • List a variable name with a type-specifier appended (count%, txt$)

  • Use the AS clause to specify the type (count AS INTEGER, text AS STRING * 100, etc.).

Legal type names for arguments include ANY, ASCIIZ, BYTE, CUR, CUX, DOUBLE, DWORD, EXT, INTEGER, LONG, PTR, QUAD, SINGLE, , WORD and ARRAY. The ARRAY keyword is used in conjunction with one of the other types to specify an entire array of that type. For example:

DECLARE SUB KerPlunk(INTEGER ARRAY, DOUBLE)

declares a procedure called KerPlunk, which takes an entire Integer array and a Double-precision variable as parameters. You can also name the parameters using the AS keyword:

DECLARE SUB KerPlunk(iArray() AS INTEGER, dVar AS DOUBLE)

The following four declare statements are equivalent:

DECLARE SUB KerPlunk(x) ' if DEFINT A-Z is in effect

DECLARE SUB KerPlunk(x%)

DECLARE SUB Plunk(x AS INTEGER)

DECLARE SUB KerPlunk(INTEGER)

When parameters are passed by reference (BYREF), the full 32-bit address of the variable passed to the routine is placed on the stack.

Using ANY disables type checking for a given parameter, and passes the full 32-bit address of the variable passed on the stack. Since the internal format of variables differ greatly by type, you must use caution to be absolutely certain your code knows the data type in each invocation. Normally, a second parameter is used to specify the actual type of the ANY parameter.

You can use the BYVAL or BYREF keywords to specify that a given parameter should always be passed by value or by reference, respectively.

When a Sub/Function definition specifies either a BYREF parameter or a pointer variable parameter, the calling code may freely pass a BYVAL DWORD or a instead. While the use of the explicit BYVAL override in the calling code is optional, it is recommended for clarity. It is necessary to explicitly declare all pointer parameters as BYVAL (BYVAL x AS BYTE PTR). Failure to do so will generate compile-time Error 549 ("BYVAL required with pointers").

Additional information on BYVAL/BYREF/BYCOPY parameter passing can be found in the CALL statement topic.

Optional parameters

PowerBASIC now supports two syntax formats for optional parameters: the classic optional parameter syntax using brackets "[..]", and the new syntax using the OPTIONAL (or OPT) keyword. We'll discuss each one in turn.

Using OPTIONAL/OPT

DECLARE statements may specify one or more parameters as optional by preceding the parameter with either the keyword OPTIONAL (or the abbreviation OPT). Optional parameters are only allowed with CDECL or SDECL calling conventions, not BDECL.

When a parameter is declared optional, all subsequent parameters in the declaration are optional as well, whether or not they specify an explicit OPTIONAL or OPT directive. The following two lines are equivalent, with both second and third parameters being optional:

DECLARE SUB sABC(a&, OPTIONAL BYVAL b&, OPTIONAL BYVAL c&) AS LONG

DECLARE SUB sABC(a&, OPT BYVAL b&, BYVAL c&) AS LONG

When optional parameters are omitted in the calling code, the stack area normally reserved for those parameters is zero-filled, so BYVAL parameters default to the value 0. String, User-Defined Type (UDT), and Union variables may also be specified as OPTIONAL BYVAL parameters.

However, care should be exercised with OPTIONAL BYREF parameters, as an attempt to reference a missing value will cause a GPF (like trying to use a null pointer). If OPTIONAL BYREF parameters are used, it is the programmer's responsibility to avoid this situation by passing other information which defines which optional parameters, if any, have actually been passed.

The OPTIONAL directive provides the same functionality as the older syntax using square brackets "[..]". See below.

Using classic optional parameters

When declaring a CDECL SUB or FUNCTION, you can specify trailing parameters as optional, using a set of brackets [..]:

DECLARE SUB KerPlunk CDECL (x%, y% [, z%])

Note that the comma separating the y% parameter from the optional z% parameter is inside the brackets. The following calling sequences would then be valid:

CALL KerPlunk (x%, y%)

CALL KerPlunk (x%, y%, z%)

Optional parameters must be the last parameters designated in the list. The following is invalid:

DECLARE SUB KerPlunk CDECL ([x%,] y%, z%)

Because the SUB or FUNCTION being called does not know how many parameters are being passed at the time it is called, you should pass the number of parameters as one of the required parameters in the list.

PowerBASIC continues to support the use of classic optional parameter syntax using brackets ([..]) but this will not be the case in future versions of PowerBASIC. Existing code should be changed to the new OPTIONAL syntax as soon as possible to ensure compatibility with future versions of PowerBASIC.

AS type

You may specify the type of data returned by a Function to the calling code. If you do not specify a type, PowerBASIC assumes that the Function returns the data type specified by a DEFtype statement. However, if no DEFtype or AS type has been specified, a compile-time error is generated.

Therefore, there are two ways to specify the return type of a Function:

  • Include a type-specifier character at the end of ProcName

  • Include the AS type clause as the last part of the DECLARE statement (this is the recommended syntax to ensure compatibility with future versions of PowerBASIC).

For example, the following statements are equivalent:

DECLARE FUNCTION aFunction?()

DECLARE FUNCTION aFunction() AS BYTE

Restrictions

A Sub/Function may be imported and exported within the same module. That is, a function in the module may be stated as EXPORT, while a DECLARE in the same module specifies it as an imported function by the option LIB "filename.dll", as long as FILENAME.DLL is the name of the module. This may be particularly valuable when you wish to build an #INCLUDE file with all of the DECLARE statements for a project.

See also

CALL, CALL DWORD, FUNCTION/END FUNCTION, SUB/END SUB

Example

' Main program

DECLARE SUB Calculate LIB "A.DLL" (EXT, CUR, QUAD, INTEGER)

…

CALL Calculate(w##, x@, y&&, z%)


DECR statement

Purpose

Decrement a variable by 1; Decrement a pointer by the size of its target; or decrement the target of a numeric by 1.

Syntax

DECR variable

Remarks

variable can be a numeric variable or a pointer variable. When DECR is used with a numeric variable, 1 is subtracted from the numeric variable.

If DECR is used on the target of a numeric pointer variable (i.e., DECR @IntPtr), the target numeric variable is decremented by one. However, when using DECR on a pointer variable, the value of the pointer variable is decremented by the size of its target.

For example, given a pointer to element 1000 of an Integer array, DECR of the pointer variable itself would result in a decrement of 2, which should point to the previous element in the array (element 999). This is because an Integer is two bytes wide, so the pointer value is reduced by 2 bytes.

See also

INCR

Example

DIM x&, LongPtr AS LONG POINTER

DECR x&

DECR LongPtr

DECR @LongPtr


DEFBYT, DEFCUR, DEFCUX, DEFDBL, DEFDWD, DEFEXT, DEFINT, DEFLNG,

DEFBYT, DEFCUR, DEFCUX, DEFDBL, DEFDWD, DEFEXT, DEFINT, DEFLNG, DEFQUD, DEFSNG, DEFSTR and DEFWRD functions

Purpose

Declare the default type for variable identifiers that begin with specified letters.

Syntax

DEFtype letter_range [, letter_range] [, ...]

Remarks

type represents one of PowerBASIC's variable types: INT (Integer), LNG (Long-integer), QUD (Quad-integer), SNG (Single-precision floating-point), DBL (Double-precision floating-point), EXT (Extended-precision floating-point), CUR (Currency), CUX (Extended-currency), STR (String), BYT (Byte), WRD (Word), and DWD (Double-word).

letter_range

is either a single alphabetic character (A through Z, case insignificant), or a range of letters (two letters separated by a hyphen, for example, A-M).

DEFtype

tells the compiler that variables and user-defined functions, whose names begin with the specified letter or range of letters, are of the specified type.

Normally, when the compiler finds a variable name without a type specifier, a compiler error is generated. If however, there was a preceding DEFtype statement such as DEFINT A-Z, the variable would default to that type (in this case, an Integer variable).

You may use multiple DEFtype statements. If there is overlap between two DEFtype statements, no error is generated; but the definition of the latter DEFtype statement overrides the former where the two overlap.

The DEFtype statement may not be supported in future editions of PowerBASIC, so we recommend explicit variable declarations, using DIM, LOCAL, STATIC, THREADED, or GLOBAL.

Restrictions

Deftype only applies to implicitly-defined variables. It has no effect on variables that are defined explicitly. If a #DIM ALL statement exists in the application then Deftype statements will have no affect, #DIM ALL requires all variables to be defined explicitly

Example

DEFINT A-E, G, Q, Y-Z

DEFCUX B, F, H-P, R-X

FUNCTION PBMAIN

A = 1 ' A is Integer

B = 2 ' B is Extended-currency.

...

END FUNCTION

See Also

#DIM ALL, DIM, LOCAL, STATIC, THREADED, GLOBAL


DESKTOP GET CLIENT statement

Purpose | Retrieve the size of the client area of the desktop, in pixels.
Syntax | DESKTOP GET CLIENT TO ncWidth&, ncHeight&
Remarks | The desktop client size is the part of the screen that is not obscured by the system tray. This can be used in combination with DESKTOP GET LOC or DESKTOP GET SIZE for exact positioning of windows on the desktop.
See also | DESKTOP GET LOC, DESKTOP GET SIZE


DESKTOP GET LOC statement

Purpose

Retrieve the location of the top, left corner of the client area of the desktop, in pixels.

Syntax

DESKTOP GET LOC TO x&, y&

Remarks

The desktop client area is the part of the screen that is not obscured by the system tray. The system tray's position on the screen determines the upper, left position of the client area. If the tray is located at the bottom of the screen (default), left and top coordinates are 0,0. If the tray is located on the right side of the screen, left and top coordinates are 0,0. If the tray is located on the left side of the screen, left and top coordinates are TrayWidth,0. If the tray is located at the top of the screen, left and top coordinates are 0,TrayHeight.

This can be used in combination with DESKTOP GET CLIENT or DESKTOP GET SIZE for exact positioning of windows on the desktop.

See also

DESKTOP GET CLIENT, DESKTOP GET SIZE


DESKTOP GET SIZE statement

Purpose | Retrieve the size of the entire desktop, in pixels.
Syntax | DESKTOP GET SIZE TO ncWidth&, ncHeight&
Remarks | The desktop size includes the space taken up by the system tray and is same as the screen size. This can be used in combination with DESKTOP GET CLIENT or DESKTOP GET LOC for exact positioning of windows on the desktop.
See also | DESKTOP GET CLIENT, DESKTOP GET LOC


DIM statement

Purpose

Declare and dimension arrays, scalar variables, and .

Syntax

Arrays:

DIM var[(subscripts)] [AS [GLOBAL | LOCAL | STATIC | THREADED] type] [PTR | POINTER] [AT address] [, ...]

DIM var[(subscripts)] ' var may include a type-specifier

Scalar variables:

DIM var AS [GLOBAL | LOCAL | STATIC | THREADED] type [PTR | POINTER] [, ...]

DIM var ' var must include a type-specifier

Remarks

DIM declares var to be a variable or array whose type is specified by appending a type-specifier to the name or by using the AS type keyword. If the AS clause is used, the variable name cannot end with a type-specifier character.

DIM can only be used inside a SUB or FUNCTION. Outside of Subs and Functions, use GLOBAL to declare variables and arrays.

DIM can also be used to dimension an "absolute array" - one that occupies a specific location in memory. This can be useful to dynamically "superimpose" one type of array directly over the top of an existing block of memory (which could be another type of array, or data structure). This would form a Union-like overlay structure. See below.

In addition, it is possible to create an array of pointers with the DIM statement, and it is also possible to do so at a specific location in memory. This is termed an "absolute pointer array".

Dimensioning arrays

subscripts may take one of the following forms for each array dimensioned:

(a) A comma-delimited list of one or more Long-integer expressions, each defining a dimension of the array. This form is used to declare arrays whose subscript (index) range starts at 0. For example, the following lines are equivalent ways of dimensioning the same array:

DIM lArray(20) AS LONG ' With an AS type clause

or

DIM lArray&(20) ' With a type-specifier

Both lines above define a one dimension Long-integer array that has 21 elements, from lArray(0) to lArray(20) inclusive. The second line uses a type-specifier symbol to specify the , and this uses a simplified syntax (trailing clauses/keywords are not permitted). The simplified syntax is only valid for data types that have a type-specifier symbol ($, !, @, @@, #, ##, %, &, &&, ?, ??, ???), or the specifier can be omitted if there is a DEFtype statement in effect. The specifier must be omitted if #DIM ALL is in effect.

Declarations of multiple-dimension arrays take the following forms:

DIM sArray(20,40,2) AS STRING

or

DIM sArray$(20,40,2)

These two lines of code define a dynamic string array with three dimensions, 21 elements by 41 elements by 4 elements, totaling 2583 string elements. As before, the second line uses the simplified syntax form.

(b) A comma-delimited list where both the upper and lower subscript bounds are explicitly declared for each dimension of the array. For each dimension, the lower bound is listed first, followed by a colon (:) character or the TO keyword (preferred), followed by the upper bound. For example:

DIM MyArray(1:20) AS LONG ' Classic syntax

or

DIM MyArray(1 TO 20) AS LONG ' Preferred syntax

…both define an array of one dimension that has 20 elements, from MyArray(1) to MyArray(20). The lower bound does not have to be zero or one; for example:

DIM SalesByYear(1980 TO 2000) AS INTEGER

or

DIM SalesByYear%(1980 TO 2000)

Use the TO keyword instead of the colon (:) syntax, as the colon syntax may not be supported in future versions of PowerBASIC.

Each array can have up to 4,294,967,295 elements in the range of -2,147,483,648 to 2,147,483,647 (Long-integer range). It is recommended that an explicit variable scope clause (GLOBAL/LOCAL/STATIC) be added to each DIM statement that uses an explicit type clause. See Restrictions below.

Array Initialization and Absolute Arrays

PowerBASIC generates an error message when it encounters an array that hasn't been dimensioned. If the array has already been dimensioned, the DIM statement is ignored. A new array is not created and a run-time error is not generated.

When a program is first executed, PowerBASIC sets each element of a numeric array to zero, and sets each element of regular string arrays to a null string (length zero). However, when an absolute array is Dimensioned (at a specific location in memory using the AT address syntax), PowerBASIC does not initialize the memory occupied by the array. Further, when an absolute array is erased, the memory is not released either. This provides a powerful mechanism to create Union-like overlay structures in memory.

The most common use of an absolute array is when manipulating Visual Basic arrays directly from a DLL. This involves obtaining a pointer to the array, the element size, and the number of elements. With this information, an absolute array can be dimensioned in PowerBASIC and the array memory manipulated directly. Another common use involves using a large dynamic or fixed-length string memory block, overlaid with an absolute numeric array.

Care must be exercised when using absolute arrays, since the contents of an absolute array can only be valid for the scope of the memory the array references. If an absolute array references memory that is LOCAL to the sub/function, the array contents become invalidated if the target memory block is released. For example, by either explicitly deallocating the memory block, or exiting the Sub/Function itself. Attempting to access absolute array memory that has been deallocated will likely trigger a General Protection Fault (GPF). On this basis, absolute arrays should be LOCAL to the Sub/Function in which they are to be used.

While PowerBASIC supports LBOUND values that are non-zero, PowerBASIC generates the most efficient code if the LBOUND parameter is omitted (i.e., the array uses the default LBOUND of zero). You should also avoid specifying an explicit LBOUND of zero, since this imposes a small efficiency penalty with no meaningful benefits

Declaring scalar (non-array) variables

If you have specified #DIM ALL or OPTION EXPLICIT, you have to declare all variables used in your programs. PowerBASIC provides a variation of the DIM statement for this job, because of the reduced level of syntax required for scalar variables. The following is a simplified syntax for DIM that just applies to scalar variables:

DIM var AS [GLOBAL | LOCAL | STATIC | THREADED] type [PTR |

POINTER] [, ...]

DIM var ' var must include a type-specifier

Here are some sample variable declarations:

DIM a AS LOCAL INTEGER

DIM b AS STATIC WORD

DIM c AS GLOBAL DOUBLE POINTER

DIM d AS ASCIIZ * 255

DIM e AS THREADED STRING

AS type

Type

BYTE

Byte

WORD

Word

INTEGER

Integer

DWORD

Double-word

LONG

Long-integer

QUAD

Quad-integer

SINGLE

Single-precision floating-point

DOUBLE

Double-precision floating-point

EXT

Extended-precision floating-point

EXTENDED

Extended-precision floating-point

CUR

Currency

CURRENCY

Currency

CUX

Extended-currency

CURRENCYX

Extended-currency

STRING

Dynamic (variable-length) string

STRING * x

Fixed-length string

ASCIIZ * x

Nul-terminated string

ASCIZ * x

Nul-terminated string

Pointer

Pointer

VARIANT

Variant

DISPATCH

Dispatch

GUID

16-byte GUID string

FIELD

Field string

Restrictions

LOCAL ASCIIZ and LOCAL fixed-length strings are created on the stack frame of the Sub/Function in which they are declared, limiting the combined size of such strings to typically less than 1 MB. If larger ASCIIZ or fixed-length strings are required, use a STATIC or GLOBAL type instead, since these are not stored on the stack frame. Also see #STACK.

When a DIM statement is used (without an explicit scope clause), to declare a variable in a Sub or Function, and an identical variable has already been declared as GLOBAL, the variable in the Sub/Function will be given GLOBAL scope. For example:

GLOBAL xyz AS LONG

...

SUB MySub

DIM xyz AS LONG

' Here, xyz is a GLOBAL variable

END SUB

To ensure that the variable scope is LOCAL to the Sub/Function, use a LOCAL statement rather than a DIM statement. Alternatively, add an explicit scope clause to the DIM statement. For example:

GLOBAL xyz AS LONG

...

SUB MySub

DIM xyz AS LOCAL LONG

' Here, xyz is a LOCAL variable

END SUB

Declaring pointer variables

A pointer must be declared before it can be used. You use the DIM statement to declare pointers, and describe the type of data to which they point. When a pointer is declared, it is automatically initialized to a value of zero. This is known as a null-pointer. You must remember to initialize it to a valid address, or you will get a General Protection Fault (GPF). The syntax for declaring pointer variables is similar to that of regular variables:

DIM var[(subscripts)] AS [GLOBAL | LOCAL | STATIC | THREADED]

type [PTR | POINTER] [, ...]

Here are some examples of pointer variable declarations:

DIM a AS BYTE PTR

DIM b AS INTEGER POINTER

DIM c AS STRING PTR * 25

DIM d AS MyType POINTER

DIM e(500) AS INTEGER PTR

Pointers themselves are stored as DWORD values.

Options

The scope of a variable or array is set using the GLOBAL, LOCAL, STATIC, or THREADED keywords.

Restrictions

When returning a pointer to a calling Sub or Function, make sure the pointer target remains valid when the current Sub or Function terminates. For example, returning a pointer to a LOCAL variable is certain to trigger a GPF, since local storage is released when the Sub/Function ends. In this case, the pointers target should be STATIC or GLOBAL, or be valid within the scope of the calling code.

DISPATCH, VARIANT and GUID variables have special uses with COM client/controller application code. Please see the COM Programming Introduction for more information.

See also

#DIM, ARRAYATTR, ERASE, GLOBAL, LOCAL, REDIM, RESET, STATIC, THREADED, Variables, Variable Scope


DIR$ CLOSE statement

Purpose

Force the release of the operating system FindNext handle.

Syntax

DIR$ CLOSE

Remarks

DIR$ CLOSE will cause the operating system FindNext handle to be closed. Each time a new DIR$() sequence is initiated within a thread, or DIR$() returns an empty string, PowerBASIC automatically closes the FindNext handle to avoid overuse of system resources.

However, in unusual circumstances (such as a recursive directory scan with delete), it may be necessary to close the FindNext handle sooner, through the use of this explicit statement, so that a directory can be removed.

Restrictions

It is never necessary to execute DIR$ CLOSE to simply avoid a "System Handle Leak".

See also

DIR$


DIR$ function

Purpose

Return a filename that matches the given mask and optional attribute.

Syntax

f$ = DIR$
f$ = DIR$(mask$ [, attribute])

Remarks

mask$ specifies a filename or path which can include a drive name and DOS compatible wildcard characters (* and ?). If the numeric attribute parameter is not specified, DIR$ returns the name of the first file that matches mask$.

Further calls to DIR$ with no parameters returns subsequent matching filenames.

When the returned is null (zero-length), there are no further matching filenames to retrieve. If mask$ is a null (zero-length) string, the function call is equivalent to DIR$ with no parameters.

DIR$ can use, and return, Long File Names (LFNs).

If attribute is specified, it must use a standard operating system numeric attribute code. This causes DIR$ to include filenames with specific attributes in the search, in addition to regular files. "Regular" files are files that do not have their hidden or system attributes set; however, regular files do include read-only and archived files.

You can search for files with multiple attributes set by adding the attribute codes together. For example, to search for hidden and system files, you'd add those codes together (2 and 4) to get 6. All other attribute codes except for volume label are inclusive, for example: specifying both hidden and system results in DIR$ returning all hidden files, system files, regular files, and files that are both hidden and system.

Specifying directories will return all subdirectory names as well as regular files. You can use the GETATTR function to distinguish subdirectories from files.

A useful search attribute is 23, which returns subdirectories as well as System, Hidden and Read-only files. To return files without directories, use a search attribute of 7.

Specifying a volume label will return only the volume label, if any. In this case, mask$ must reference the drive letter of the target drive, and any additional path information is ignored. For example:

' Retrieve the disk volume for Drive C:

A$ = DIR$("C:", 8)

' Retrieve the disk volume for Drive H:

A$ = DIR$("H:\*.*", %VLABEL)

Additionally, you may specify a UNC name for a shared drive (subject to operating system restrictions), and retrieve the volume label, if one exists, and you have suitable access rights. You can also obtain volume label for a 'hidden' share with NT/2000 PC's, by appending a trailing dollar symbol to the share name.

' Retrieve volume for share \\server\drive0

A$ = DIR$("\\server\drive0", 8)

' Retrieve volume for hidden share D: (\d$)

A$ = DIR$("\\server\d$", %VLABEL)

The following table identifies equates that can be used to make your code more easily read:

Attribute | Description | Equate*
0 ** | Normal | %NORMAL
1 ** | Read-only | %READONLY
2 | Hidden | %HIDDEN
4 | System | %SYSTEM
8 *** | Volume Label | %VLABEL
16 | Directory | %SUBDIR
32 ** | Archived | %ARCHIVE

* Appropriate equates are defined in the WIN32API.INC file.

** These attributes are ignored by DIR$. DIR$ always returns read-only and archived files, as they're considered "regular".

*** If the attribute includes 8, the attribute code for the volume label, only the volume label will be returned (if one exists). Regular files aren't included in the search. Further calls to DIR$ will return an empty string, even if the original call included other attribute codes.

Restrictions

PowerBASIC performs file matching with both the long (LFN) and short (SFN) filename versions of filenames. This means that DIR$ will also return filenames that start with the specified extension (as per standard Windows operating system behavior).

For example, A$ = DIR$("*.htm") will match filenames such as "Index.htm", "Default.html", "Homepages.htmb", "cgilib.htmlpages", etc. Similarly, A$ = DIR$("*.h??") and DIR$("*.ht*") will match the same filenames.

DIR$ is thread-safe, so DIR$ operations in one thread do not interfere with DIR$ operations in another thread.

However, you should be aware that changing the mask during a DIR$ search loop (within the same thread) will affect the search loop operation. That is, if a typical DIR$ search loop block such as shown in the Example code below, changes the search mask mid-way through the loop, subsequent calls to DIR$ will begin to return files matching the new mask.

This is rarely a problem if the mask change takes place within the search loop code block, since the DIR$("mask") change will be plainly visible in the loop block code. However, the mask can also be changed from within a Sub/Function that is called from within the search block (provided that the Sub/Function is located within the same compiled module).

While such techniques are perfectly valid, the cause of the mask change may not be immediately obvious when viewing just the search loop code block. PowerBASIC allows the mask to be changed in this manner, since it may be an intended behavior on the part of the programmer, but in doing so, it opens up the potential for subtle bugs to be introduced into your code if this technique is not anticipated.

See also

CURDIR$, DIR$ CLOSE, FILEATTR, GETATTR, SETATTR

Example

The following code shows a typical method of retrieving filenames from a directory:

DIM Listing$(1 TO 1000)

DIM x&

A$ = DIR$("*.*")

WHILE LEN(A$) AND x& < 1000 ' max = 1000

INCR x&

Listing$(x&) = A$

A$ = DIR$

WEND


DISKFREE function

Purpose | Return the amount of available space on a disk, in bytes.
Syntax | bytes&& = DISKFREE(drive$)
Remarks | drive$ specifies the drive letter or UNC share name (subject to operating system restrictions) of the disk to examine. If drive$ is an empty string, information on the default drive is returned.
Restrictions | With Windows 95 versions before OSR2, and Windows NT versions before 4.0, DISKFREE may return a negative or inaccurate value for drives larger than 2 GB.
See also | DISKSIZE
Example | DisplayText "Free bytes on C: " + FORMAT$(DISKFREE("C"), "#,###")


DISKSIZE function

Purpose | Return the total amount of space on a disk, in bytes.
Syntax | bytes&& = DISKSIZE(drive$)
Remarks | drive$ specifies the drive letter or UNC share name (subject to operating system restrictions) of the disk to examine. If drive$ is an empty string, information on the default drive is returned.
Restrictions | With Windows 95 versions before OSR2, and Windows NT versions before 4.0, DISKSIZE may return a negative or inaccurate value for drives larger than 2 GB.
See also | DISKFREE
Example | DisplayText "Total bytes on C: " + FORMAT$(DISKSIZE("C"), "#,###")


DO/LOOP statements

Purpose

Define a group of program statements that are executed repetitively as long as a certain condition is met.

Syntax

DO [{WHILE | UNTIL} expression]

...

{statements}

...

[EXIT LOOP]

[ITERATE LOOP]

...

LOOP {{WHILE | UNTIL} expression}

Remarks

expression is a numeric expression, in which non-zero values represent logical TRUE, and zero values represent logical FALSE. If a string expression is used (i.e., A$ <> ""), PowerBASIC returns TRUE if the length of result of the string expression is greater than zero.

DO/LOOP statements are extremely flexible. They can be used to create loops for almost any imaginable programming situation. They allow you to create loops with the test for the terminating condition at the top of the loop, the bottom of the loop, both places, or none of the above.

A DO statement must always be paired with a matching LOOP statement at the bottom of the loop. Failure to match each DO with a LOOP results in either a compile-time Error 448 ("DO loop expected") or an Error 456 ("LOOP/WEND expected").

The WHILE and UNTIL keywords are used to add tests to a DO/LOOP. Use the WHILE if the loop should be repeated if expression is TRUE, and terminated if expression is FALSE. UNTIL has the opposite effect; that is, the loop will be terminated if expression is TRUE, and repeated if FALSE.

For example:

DO WHILE a = 13

{statements}

LOOP

executes the statements between DO and LOOP as long as a is 13. If a is not 13 initially, the statements in the loop are never executed. Conversely:

DO UNTIL a = 13

{statements}

LOOP

executes the statements between DO and LOOP as long as a is not 13. If a equals 13 initially, the loop is never executed.

At any point in a DO/LOOP, you can include an EXIT LOOP or ITERATE LOOP statement. EXIT LOOP causes the loop to terminate, so that execution continues after the terminating loop statement. ITERATE LOOP causes the loop to continue at the terminating loop statement.

The WHILE/WEND statements can be used in many cases to perform the same functions as DO/LOOP. For example, this DO/LOOP:

DO WHILE a < b

{statements}

LOOP

has the same effect as this WHILE/WEND loop:

WHILE a < b

{statements}

WEND

When using nested loops, be careful that inner loops do not modify variables that are used by the outer loop's terminating condition test. For example, the following code was intended to get all 20 elements of a 10x2 array (dimensioned arry(9,1)):

Count1 = 0

DO WHILE Count1 < 10

FOR Count2 = 0 TO 1

x = arry(Count1,Count2)

Count1 = Count1 + 1

NEXT Count2

LOOP

Because Count1 is incremented within the inner loop, which executes twice for each pass through the outer loop, this code would not get all the array values, but would only get the values for arry(0,0), arry(1,1), arry(2,0), arry(3,1) and so on. By moving the Count1 = Count1 + 1 statement to just below the NEXT Count2 statement, the code functions as intended.

If an EXIT LOOP statement is used within nested loops, it exits only the current loop, not the entire nest. Similarly, an ITERATE within nested loops iterates the current loop. For advice on exiting nested block structures, please refer to the EXIT statement. The PowerBASIC can be used to construct multiple test conditions for loop control. For example:

DO WHILE x < 10 AND y < 10

{statements}

LOOP

is executed only as long as both x and y are less than 10. Similarly, the loop:

DO UNTIL X > 10 OR Y > 10

{statements}

LOOP

is executed until either x or y (or both) is (are) greater than 10. See the and Arithmetic Operators topics for more information about using logical operators.

Although the compiler doesn't care about such things, it is a good idea when writing your source code to indent the statements between DO and LOOP. The same is true of FOR/NEXT loops, WHILE/WEND loops, and multi-line IF statements. Such indenting makes the appearance of your source code reflect the logical structure of your program, resulting in greater readability. Indenting is particularly valuable when nesting multiple loops of the same type, since it makes it easier to see which LOOP goes with which DO.

Also see the discussion on the IF statement for notes on PowerBASIC's Short-circuit evaluation and its possible side effects.

See also

EXIT, FOR/NEXT, , ITERATE, WHILE/WEND


END statement

Purpose

Terminate program immediately.

Syntax

END [nErrorLevel&]

Remarks

Normally, PowerBASIC programs are terminated when you exit the PBMAIN or WINMAIN() function. It should always be your goal to end programs in this fashion, so that the compiler and the operating system can do everything possible to leave things in an orderly state.

That said, PowerBASIC offers limited support for the END statement, in order to provide an additional level of compatibility with DOS programs and simplify conversion efforts.

The optional nErrorLevel& value has an effective range of 0 to 255. Batch files may act on the result through the IF [NOT] ERRORLEVEL batch command.

Restrictions

END is intended only for temporary use in converting DOS programs to Windows. You should convert it to the standard EXIT FUNCTION method as soon as possible. END must never be used in a program which uses COM access.

See also

EXIT, PBMAIN, WINMAIN


ENVIRON$ function

Purpose

Retrieve information from the current program’s environment table.

Syntax

s$ = ENVIRON$({parameter_string | n})

Remarks

parameter_string is a string expression denoting which environment parameter is to be retrieved. n is an expression, starting at 1.

If a string argument is used, ENVIRON$ returns the text that follows parameter_string (after the equal sign) in the environment table. If parameter_string is not found, or no text follows the equal sign in the environment string table, an empty string is returned.

If the numeric argument is used, it acts as an index into the environment table. ENVIRON$ returns a string containing the nth parameter from the start of the table. If there is no nth parameter, an empty string is returned. If the index is negative, private Windows variables are returned.

When launching a program from within the IDE, PowerBASIC sets the "PBIDE" environment variable with the IDE name and version number. For example, "CCEDIT 4.00" or "PBEDIT 8.00". Similarly, when running in the debugger, the "PBDEBUG" environment string will return the IDE name and version.

Programs can use these environment strings to detect their "mode" of operation, for example, to signal a program to save internal data to a disk file, and when to display helpful debugging information. DLLs created with PB/Win can also examine these environment strings and adapt behavior accordingly. This will be of particular interest to 3rd-party DLL programmers who create libraries and add-ons for other PowerBASIC programmers.

Restrictions

When a program (process) starts, it is given its own local environment table, which is typically a copy of the parent program’s environment table. ENVIRON$ works with this local table, not the parent’s table.

See also

ENVIRON statement

Example

' Retrieve the PATH environment variable

Path$ = ENVIRON$("PATH")

IF LEN(ENVIRON$("PBDEBUG")) THEN _

CALL DisplayMyDebugData()

' Enumerate all Environment strings

RESET x&

DO

INCR x&

a$ = ENVIRON$(x&)

' process a$ here

LOOP WHILE LEN(a$)


ENVIRON statement

Purpose | Modify the current program's environment table.
Syntax | ENVIRON envstring$
Remarks | Modify the environment table for the current program and any subsequent child programs that are launched. A single string expression parameter sets both the name of the environment variable and its value, delimited by an equal ("=") sign. If a value is not specified, the variable is removed from the environment table.
See also | ENVIRON$ function
Example | ENVIRON "SETMODE=YES" ' SETMODE = "YES" ENVIRON "SETMODE=" ' Removes SETMODE


EOF function

Purpose

Return the end-of-file status of an opened file or TCP/UDP transmission.

Syntax

y = EOF(filenum&)

Remarks

Use EOF to determine when the end of a file has been reached while reading its data. filenum& is the file number specified when the file was Opened. EOF returns -1 (TRUE) if the end of the specified file has been reached, or if an error occurs trying to check for the end of the file. Otherwise, EOF returns 0 (FALSE).

If filenum& is not a valid, open file, a run-time Error 53 will occur ("File not found"). If filenum& is for a binary file, EOF returns TRUE only if the most recent file operation was a read operation, and that operation could not read the requested number of bytes.

The EOF function may also be used with the COMM LINE and TCP LINE statements to detect that an incomplete line was received. Normally, these statements read data until a $CRLF character pair is found, and in that case, EOF will return 0 (FALSE). However, even if no $CRLF has been found, the statements will end when no additional data is available. In that case, they will return whatever data has already been accumulated, and set EOF to -1 (TRUE).

In many cases, it would be prudent to test EOF after every COMM LINE and TCP LINE to verify that a full line has been received. In some cases, you may wish to execute the statement one or more additional times, combining the data, in order to obtain a full line of text.

See also

COMM LINE, LOC, LOF, OPEN, TCP LINE

Example

' Open an ASCII text file and read it

hFile = FREEFILE

OPEN "TEXTFILE.TXT" FOR INPUT AS hFile

WHILE ISFALSE EOF(hFile)

LINE INPUT# hFile, x$

WEND

CLOSE hFile


EQV operator

Purpose

The EQV operator works as both a logical and a bitwise arithmetic operator.

Syntax

p EQV q

Remarks

Using EQV as a logical operator

EQV returns TRUE (non-zero) if at least one bit in one operand contains the same value as the identical bit position in the other operand. Further, EQV will return zero if and only if there are no matching bit values between the two operands. This can occur when one operand is equal to the bitwise NOT value of the other operand. For example:

IF x EQV y = 0 THEN ...

…is equivalent to:

IF x = NOT y THEN ...

The EQV operator can be used for comparing signed and unsigned values of the same bit size, such as Long-integer and Double-word. This use of EQV is similar to using the BITS functions; however, care must be exercised to test the return value of EQV correctly, since EQV will return an unsigned value with all bits set only if the bit patterns of the two operands are an exact match.

The EQV truth table looks like this:

Truth table
x | y | x EQV y
T | T | T
T | F | F
F | T | F
F | F | T

Using EQV as a bitwise arithmetic operator

The EQV operator is seldom used as a bitwise arithmetic operator, but here is an example:

See also

Arithmetic Operators, AND, IMP, ISFALSE, ISTRUE, NOT, OR, XOR

Example

IF (Var1& EQV Var2???) = BITS???(-1&) THEN ...

IF (Val1% EQV Var2??) = &H0FFFF?? THEN ...

IF -1& EQV BITS???(-1&) = &H0FFFFFFFF THEN ...

IF -1% EQV BITS??(-1%) = &H0FFFF THEN ...


ERASE statement

Purpose

Deallocate array memory and release it from memory.

Syntax

ERASE array[()] [, array[()]] ...

Remarks

Any memory assigned to the individual elements (if they are dynamic strings, Objects, Variants, etc.) is also released. ERASE deallocates all the memory for LOCAL, STATIC, and GLOBAL arrays. After an array is erased, attempting to access the array may produce a General Protection Fault (GPF). Local arrays are implicitly erased upon exit from the Sub/Function that created them.

array

The name of the array to deallocate. Parentheses are optional, but are recommended for clarity of the source code.

One method to check if an array has been dimensioned without triggering a GPF is to use the LBOUND and UBOUND functions, as follows:

IF UBOUND(array) - LBOUND(array) = -1 THEN

' array() is not allocated

END IF

ERASE can deallocate an array that was passed as a parameter to a Sub or Function, but only if the array was passed by reference (BYREF). To clear the contents of an array back to its initialized state, use REDIM or RESET.

Restrictions

Absolute arrays (those created by DIM...AT) are handled differently by ERASE. An explicit ERASE will release the individual elements of an absolute array, if needed, but the full data block is left as-is because no assumptions can be made as to its origin. It is the programmer's responsibility to ensure that the memory block overlaid by the absolute array is handled correctly. In an implied ERASE (Sub/Function exit) of a LOCAL absolute array, the internal array descriptor is deactivated, but no changes of any kind are made to the individual data elements or the full block. RESET may be used to set arrays to zeroes or empty strings without releasing the data block.

See also

ARRAYATTR, DIM, REDIM, RESET

Example

ERASE Array1$(), MyArray%()


ERL system variable

Purpose | Return the last line number encountered before the most recent error.
Syntax | nline = ERL
Remarks | Return the last (most recent) line number that was encountered before the most recent run-time error, within the current Sub/Function. With ERL, line numbers are of the traditional-basic line numbering variety, not the physical source code line.
See also | ERR, ERRCLEAR, ERROR, ERROR$, Error Overview, Error Trapping, ON ERROR, Run-time Errors
Example | ERRCLEAR NAME "a nonexisting filename.txt" AS "abc.txt" IF ERR THEN lErrLine = ERL


ERR and ERRCLEAR system variables

Purpose

Return the error code of the most recent PowerBASIC run-time error.

Syntax

y = ERR

ERR = ErrNum

y = ERRCLEAR

ERRCLEAR

Remarks

ERR and ERRCLEAR return the error code of the most recent run-time error in the current Sub or Function. This number can be tested after any critical operation, so that appropriate error-handling code can be executed.

You can also assign a value to ERR. This is similar to executing an ERROR statement, except no error is generated. Instead, subsequent tests of ERR and ERRCLEAR reflect ErrNum. To explicitly raise an exception in a TRY/END TRY block, use the ERROR statement.

ERRCLEAR returns the error code of the most recent run-time error. In addition, it resets PowerBASIC's internal error code variable ERR to zero after you reference it. This ensures that the next time you test ERR or ERRCLEAR, you are guaranteed to get a zero, unless there has actually been an error since the last ERRCLEAR. ERR, on the other hand, does not reset the internal error code. It retains its value until it is changed, either by a run-time error or explicitly by your code (using the ERR= or ERROR =statements).

ERRCLEAR can also be used as a statement to reset ERR to zero. ERRCLEAR, when used as a statement, is equivalent to ERR = 0.

IMPORTANT: Be sure to study the Errors and Error Trapping.

Restrictions

Valid run-time error values are in the range 1 through 255. Attempting to set an error value (with the ERROR statement) outside of that range will convert the value to a run-time Error 5 ("Illegal function call").

See also

ERROR, ERROR$, Error Overview, Error Trapping, ON ERROR, Run-time Errors

Example

y = ERR ' sets y = ERR

ERR = 6 ' sets ERR to 6

y = ERRCLEAR ' sets y = ERR and ERR = 0

ERRCLEAR ' sets ERR = 0


ERROR$ function

Purpose

Return a containing the descriptive name of a specified PowerBASIC run-time error code.

Syntax

msg$ = ERROR$[(ErrNum)]

Remarks

ERROR$ returns the verbose text title of a PowerBASIC run-time error identified by ErrNum.

ErrNum must be in the range 1 to 255 inclusive. Values outside of this range return "No error". If ErrNum is not specified, ERROR$ returns the description of the current value of ERR.

See also

ERL, ERR, ERRCLEAR, ERROR, Error Overview, Error Trapping, ON ERROR, Run-time Errors

Example

a$ = ERROR$(5) ' Returns "Illegal function call"


ERROR statement

Purpose

Cause a run-time error to be generated and sets ERR to the specified error number.

Syntax

ERROR ErrNum

Remarks

ERROR ErrNum causes a run-time error to be generated and sets ERR to ErrNum. Run-time errors are only caught if you have an active ON ERROR GOTO in your code. ERROR is useful to explicitly raise an exception in a TRY/END TRY block.

Valid errors are in the range 1 through 255. Attempting to set an error value outside of that range will convert the value to a run-time Error 5 ("Illegal function call").

The compiler reserves codes 0 through 150, and 241 through 255 for run-time errors. You may freely use error codes 151 through 240 for your own purposes.

See also

ERL, ERR, ERRCLEAR, ERROR$, Error Overview, Error Trapping, ON ERROR, Run-time Errors

Example

ERROR 5 ' generates error 5 "Illegal Function Call"


EXIT statement

Purpose

Transfer program execution out of a block structure.

Syntax

EXIT [FOR | FUNCTION | IF | DO | LOOP | MACRO | SELECT | SUB | TRY]

EXIT [, EXIT] [, ...] [, ITERATE]

Remarks

The EXIT statement lets you leave a structure prematurely. Using EXIT by itself will leave the most recently executed structure. For example:

FOR ix = 1 TO 10

DO UNTIL x > 10

...

EXIT ' will exit from the DO LOOP

LOOP

NEXT

EXIT option | Structure exited
FOR | FOR/NEXT loop
FUNCTION | FUNCTION/END FUNCTION function
IF | IF/END IF block (see notes below)
DO or LOOP | DO/LOOP or WHILE/WEND loop
MACRO | MACRO/END MACRO block
SELECT | SELECT statement
SUB | SUB/END SUB procedure
TRY | TRY/END TRY block

If you want to exit a structure other than the one most recently executed, you must include that type of structure as part of the EXIT statement, as follows:

FOR ix = 1 TO 10

DO UNTIL x > 10

...

EXIT FOR ' will exit DO and FOR NEXT loop

LOOP

NEXT

Using EXIT is preferred over using GOTO.

You can exit nested loops by using multiple EXIT statements without the loop type, and separated by commas:

DO

DO

EXIT, EXIT ' exit both loops

LOOP

LOOP

You can also exit one loop and immediately iterate another:

FOR x = 1 TO 10

DO

EXIT, ITERATE

LOOP

' never gets here

NEXT

If you EXIT the PBMAIN or WINMAIN function, your program will stop running.

Care should be exercised when constructing exits within a nested IF/THEN block. For example:

IF x& THEN

IF y& THEN

{statements1}

EXIT IF

{statements2}

END IF

{statements3}

END IF

In the above nested block, the EXIT IF statement exits only the inner IF/THEN block (in which the EXIT statement is located), resulting in execution continuing at the {statements3} portion. That is, EXIT IF does not exit the overall IF/THEN structure.

Whilst EXIT can provide a convenient mechanism to escape a structure, subtle logic problems can arise when converting a nested single-line IF statement into an IF-block structure. For example:

IF x& = 1 THEN

IF y& = 82 THEN EXIT IF

y& = 12

END IF

…is not logically equivalent to:

IF x& = 1 THEN

IF y& = 82 THEN

EXIT IF

END IF

y& = 12

END IF

Restrictions

Before using EXIT to leave a Function, you should assign the desired function result before EXIT is executed. By default, the value returned by the function will be zero or an empty as determined by the Function return type. For example:

FUNCTION x() AS LONG

...

IF condition THEN

FUNCTION = -1&

EXIT FUNCTION ' return -1

END IF

...

END FUNCTION

See also

DO/LOOP, FOR/NEXT, FUNCTION/END FUNCTION, IF/END IF block, ITERATE, MACRO/END MACRO, SELECT, SUB/END SUB, TRY/END TRY, WHILE/WEND


EXP, EXP2 and EXP10 functions

Purpose

Return a base number raised to a power. The base is e for EXP, 2 for EXP2, and 10 for EXP10.

Syntax

y = EXP(n)

y = EXP2(n)

y = EXP10(n)

Remarks

EXP returns e to the nth power, where n is a numeric variable or expression and e is the base for natural logarithms, approximately 2.718282. Among other uses, this provides a simple way to obtain the value of e itself:

e = EXP(1)

EXP2(n) returns 2 to the nth power, where n is a numeric variable or expression.

EXP10(n) returns 10 to the nth power, where n is a numeric variable or expression.

The EXP functions provide a convenient alternative to the ^ operator, which works with any base. The EXP functions return results in Extended-precision.

See also

LOG, LOG2, LOG10, SQR, Arithmetic Operators


EXTRACT$ function

Purpose

Return the portion of a leading up to the first occurrence of a specified character or string.

Syntax

x$ = EXTRACT$([start,] MainStr, [ANY] MatchStr)

Remarks

start is the optional starting position to begin extracting. If start is not specified, it will start at position 1. MainStr is the string expression from which to extract. MatchStr is the string expression to extract up to. EXTRACT$ is case-sensitive.

EXTRACT$ returns a sub-string of MainStr, starting with its first character (or the character specified by start) and up to (but not including) the first occurrence of MatchStr. If MatchStr is not present in MainStr, all of MainStr is returned.

If the ANY keyword is included, MatchStr specifies a list of single characters to be searched for individually, a match on any one of which will cause the extract operation to be performed up to that character.

EXTRACT$ is especially useful when parsing a string containing arguments to a program, or when manipulating nul-terminated or delimited strings received from a routine written in another language.

The complementary function to EXTRACT$ is REMAIN$, which returns the part of the string that EXTRACT$ leaves behind. A similar function to EXTRACT$ is PARSE$, which extracts delimited substrings from a string.

See also

INSTR, JOIN$, LEFT$, LTRIM$, MID$, PARSE, PARSE$, PARSECOUNT, REMAIN$, REMOVE$, RETAIN$, RIGHT$, RTRIM$, TALLY, TRIM$, VERIFY

Example

' x$ = first command-line argument, assuming spaces,

' commas, periods, and tabs are valid delimiters

x$ = EXTRACT$(COMMAND$, ANY " ,."+CHR$(9))

' the following line returns "aba" (match on "cad")

x$ = EXTRACT$("abacadabra","cad")

' the following line returns nothing (match on first character "a")

x$ = EXTRACT$("abacadabra", ANY "cad")


FIELD statement

Purpose

Bind a field string variable to a random file buffer or a dynamic string variable.

Syntax

FIELD # filenum, nSize AS fieldvar, [FROM] nStart TO nEnd AS fieldvar [, ...]

FIELD DynamicStr, nSize AS fieldvar, [FROM] nStart TO nEnd AS fieldvar [, ...]

Remarks

In the first form, FIELD binds a field string variable to a specific sub-section of a random-access file buffer. In the second form, FIELD binds a field string variable to a specific sub-section of a dynamic string variable.

A field variable is a specific sub-section of a random access file buffer or a dynamic string variable. If the sub-section extends beyond the size of the file buffer or string, that portion of the FIELD is empty. Otherwise, it represents a fixed size string, and may be referenced as any other string variable.

When used with a file:

A random-access file buffer is automatically created for use when GET or PUT are used without a target variable. In this case, the file data is read or written using this buffer, which is accessed with one or many field variables.

If a field is defined by a single field (size) parameter, it represents the length of the field, with the start position implied by the preceding field within the statement. If two parameters are used, they represent the start (nStart) and end (nEnd) positions, indexed to one.

If a string value shorter than the declared size is assigned to a field string, it is padded with blank spaces as it is placed into the file buffer. There is no requirement to use LSET for assignment.

Finally, it should be noted that FIELD statements are tied to an open file, i.e. they are valid only as long as the file is open. Once the file is closed, any field strings that had been defined for the file will return nul (empty), not a string of the previously specified length.

LOCAL sFirst AS FIELD, sSecond AS FIELD

OPEN "ABC.TXT" FOR RANDOM AS #1 LEN=20

FIELD #1, 10 AS sFirst, 10 AS sSecond

sFirst = "0123456789"

sSecond = "9876543210"

Put #1 ' creates a record of: 01234567899876543210

When used with a dynamic string:

A field variable bound to a dynamic string works very much like a pointer, so the programmer must use care in field variable selection. For example, if you bind a GLOBAL FIELD variable to a LOCAL string variable, then attempt to reference the global string after the local is destroyed (i.e., released when the owning Sub/Function exits), a fatal exception error (GPF) is likely to occur. The same could happen after an array has been erased, or a REDIM is used to change the memory allocation.

To avoid problems with scope, it is suggested that field variables be bound only with strings within the same scope (LOCAL, GLOBAL, etc.).

LOCAL x$, sFirst AS FIELD, sSecond AS FIELD

FIELD x$, 3 AS sFirst, 3 AS sSecond

x$ = SPACE$(6) ' Allocate the space for the field

SFirst = "111"

sSecond = "222"

? x$ ' Displays 111222

x$ = "abcd"

? sFirst ' Displays abc

? sSecond ' Displays d

Restrictions

Field string variables must be explicitly declared using DIM, LOCAL, STATIC, GLOBAL, or THREADED. Attempting to bind a variable other than a declared field variable results in a compile-time Error 544 ("Field variable expected"). Field strings cannot be used in UDT or UNION structures. Attempting to do so results in a compile-time Error 485 ("Dynamic/Field strings not allowed").

See also

Field Strings, GET, PUT, TYPE/END TYPE, User-Defined Types, Unions, UNION/END UNION


FILEATTR function

Purpose

Return information about an open file.

Syntax

lResult& = FILEATTR([#] filenum&, fattr)

Remarks

filenum& is the handle of a currently open file. fattr is an integer between -3 and 3 that specifies the type of information required, according to the following table:

fattr

Definition

-3

The device type. Returns 1 for a file, 2 for a device. COMM, TCP and UDP are classified as devices.

-2

Logical first byte (base) position of a disk file. By default, PowerBASIC opens files with a default first location of 1, but this can be overridden via the BASE= clause of the OPEN statement. This function can be useful when the base is not known or when performing SEEK operations.

-1

The minimum amount of data that can be read or written at one time. For RANDOM files, it is the record length. For INPUT files, it is the input buffer length (set with LEN= in the OPEN statement). For BINARY, OUTPUT and APPEND, there is no buffering, so it always returns 1 (1 byte).

0

The open state. TRUE (non-zero) if open, FALSE (zero) if closed.

1

The file mode (which may be a combination of the following):

result& | File mode
1 | Input
2 | Output
4 | Random
8 | Append
16 | Serial Communications (COMM)
32 | Binary
64 | TCP Winsock
128 | UDP Winsock

(for example, an APPEND file will return 8 + 2 = 10).

2

The operating system file handle for the file. This handle can be used with particular Windows API calls files to manipulate files opened with PowerBASIC, and with the OPEN HANDLE statement.

3

Enumerates existing file numbers. This mode enumerates existing file numbers, in the range of 1 to 32767. FILEATTR(1,3) returns the first located file number, FILEATTR(2,3) the second, and so on until -1 is returned to indicate that there are no more file numbers active. The file numbers returned are not guaranteed to be returned in any particular sequence, nor be open. You can use FILEATTR(#filenum,0) to determine whether a given file number is open or closed.

The number symbol [#] is optional, but recommended for clarity.

See also

COMM OPEN, EOF, FILENAME$, GETATTR, LOF, OPEN, SEEK function, SEEK statement, SETATTR, TCP OPEN, UDP OPEN

Example

OPEN "TEST.DOC" FOR OUTPUT AS #1 LEN = 28

x& = FILEATTR(#1,1)

Result x& = 2


FILECOPY statement

Purpose

Copy a file.

Syntax

FILECOPY sourcefile, destfile

Remarks

Copy the file sourcefile to the file destfile. Both sourcefile and destfile must be filenames, not merely drives or directories (although it's OK to include drive and directory specifications along with the filenames). Wildcards are not supported. If you attempt to copy a file that is read locked (preventing read access), a run-time Error 70 will occur ("Permission denied").

If the destination file already exists, it will be overwritten. If it is not possible to overwrite the existing destination file (for example, it is marked as read-only or in use by another program), the result will be a run-time Error 70 ("Permission denied").

Remarks The attributes of the source file are inherited by the destination file, with the exception of the Archive attribute, which is always set ON for the destination file. File attributes may be examined or modified with the GETATTR and SETATTR statements.

See also

FILEATTR, GETATTR, SETATTR

Example

FILECOPY "C:\AUTOEXEC.BAT", "C:\AUTOEXEC.BAK"


FILENAME$ function

Purpose | Return the file-system name of an open file.
Syntax | a$ = FILENAME$(filenum&)
Remarks | a$ receives the name of the open file identified by the file number filenum&. This function is not valid with a file opened using OPEN HANDLE, COMM OPEN, TCP OPEN, or UDP OPEN.
See also | CLOSE, FILEATTR, FREEFILE, GETATTR, OPEN, SETATTR
Example | OPEN "MYFILE.TXT" FOR INPUT AS #1 a$ = FILENAME$(1) CLOSE #1


FILESCAN statement

Purpose

Rapidly scan a file opened for INPUT or BINARY mode, in order to obtain size information about variable length string data.

Syntax

FILESCAN [#] fnum&, RECORDS TO y& [, WIDTH TO x&]

Remarks

FILESCAN assigns a count of the lines/records/strings to y&, and if the WIDTH clause is specified, the length of the longest string to x&.

In INPUT mode, it is assumed the data is standard text, with lines delimited by a CR/LF ($CRLF) pair. FILESCAN stops reading the file if it encounters an "end of file" (EOF) marker byte (CHR$(26) or $EOF). Text that occurs after the last CR/LF but before the EOF is considered the last record of the file. Use the LINE INPUT# statement to read a complete text file into an array.

In BINARY mode, it is assumed the file was written in the PowerBASIC and/or VB packed string format using PUT of an entire string array. If a string is shorter than 65535 bytes, a 2-byte length WORD is followed by the string data. If a string is equal to or longer than 65535 bytes, a 2-byte value of 65535 is followed by a length DWORD value, then finally the string data.

Use the GET statement to read a complete binary file into an array.

Restrictions

If FILESCAN is applied to other file formats, the results are undefined.

See also

GET, GET$, LINE INPUT#, PUT, PUT$

Example

OPEN "datafile.dat" FOR INPUT AS #1

FILESCAN #1, RECORDS TO count&

DIM TheData(1 TO count&) AS STRING

LINE INPUT #1, TheData() TO count&

CLOSE #1

Result

The entire text file comprising y& lines is read into the string array.


FIX function

Purpose

Truncate a number to an .

Syntax

y = FIX(numeric_expression)

Remarks

FIX strips off the fractional part of its argument, and returns the integer part. Unlike CINT and INT, FIX does not perform any form of rounding or scaling.

See also

CEIL, CINT, INT, FRAC, ROUND

Example

x$ = "The integer part of 50.67 is" + STR$(FIX(50.67))

y$ = STR$(FIX(-1.1)) & ", " & STR$(INT(-1.1)) & ", " & STR$(CINT(-1.1))

Output The integer part of 50.67 is 50

-1, -2, -1


FLUSH statement

Purpose | Flush file buffers to disk, to ensure that the disk information is up-to-date.
Syntax | FLUSH [[#] filenum& [, [#] filenum&] ...]
Remarks | FLUSH ensures that all data you have written to disk files has actually been written to disk. The CLOSE statement also flushes the buffers, but FLUSH has the advantage of leaving the files open.
filenum& | The file number of an OPEN file. If filenum& is specified, only the data for that file is flushed. Otherwise, data for all open files is flushed. The Number (#) symbol is optional, but recommended for the purposes of clarity.
See also | CLOSE, OPEN


FOR/NEXT statements

Purpose

Define a loop of program statements whose execution is controlled by an automatically incrementing or decrementing counter.

Syntax

FOR Counter = start TO stop [STEP increment]

.

. {statements}

.

NEXT [Counter [, [Counter]] ...]

Remarks

Counter is a numeric variable serving as the loop counter.

start is a numeric expression specifying the value initially assigned to Counter.

stop is a numeric expression giving the value that Counter must reach for the loop to be terminated.

increment is an optional numeric expression defining the amount by which Counter is incremented with each loop execution. If not specified, increment defaults to 1.

Note that increment must be the same data type or in the same range as Counter. For example:

FOR x?? = 50 TO 1 STEP -1

will fail because -1 is not within the range of an unsigned Word variable.

When a FOR statement is encountered, start is assigned to Counter, and Counter is tested to see if it is greater than (or, for negative increment, less than) stop. If not, the statements within the FOR/NEXT loop are executed, increment is added to Counter, and Counter again tested against stop. The statements in the loop are executed repeatedly until the test fails, at which time control passes to the statement immediately following the NEXT.

If increment is equal to the maximum value of a variable class (255 for a byte, 32767 for an Integer, 65535 for a Word, etc), the compiler will generate an error. If step is zero, an infinite loop can be created.

When using values with FOR/NEXT, be sure to allow for round-off errors when mixing numbers of different precision. Using constants or variables of the same type throughout will help solve this problem:

FOR n# = 1.0 TO 1.5 STEP 0.1

x$ = STR$(n#)

NEXT n#

executes 5 times and returns:

1

1.10000000149012

1.20000000298023

1.30000000447035

1.40000000596046

while:

FOR n@ = 1.0@ TO 1.5@ STEP 0.1@

x$ = STR$(n@)

NEXT n@

executes 6 times and returns:

1

1.1

1.2

1.3

1.4

1.5

FOR/NEXT loops run fastest when Counter is a Long-integer variable, and start and increment are Long-integer constants. The value of Counter is available like any other variable within the loop. It is wise to avoid explicitly modifying the value of Counter within the loop. If you need to exit the loop prematurely, use an EXIT FOR statement. Keep range considerations in mind. For example, if Counter is an Integer variable, you may not use the maximum value for an Integer for stop, as Counter would be incremented outside the Integer range at the end of the loop.

The body of the loop is skipped altogether if the initial value of Counter is greater than stop (or, for a negative increment, if Counter is less than stop).

FOR/NEXT loops can be nested within other FOR/NEXT loops. Be sure to use unique counter variables. Note that PowerBASIC allows the Counter in the NEXT keyword simply as a comment, which is ignored. For example, the following will compile, even though the counter variables are "crossed":

FOR n = 1 TO 10

FOR m = 1 TO 20

.

.

.

NEXT n

NEXT m

You can omit the counter variable in the NEXT statement altogether. For example:

FOR n = 1 TO 10

.

.

.

NEXT

A single NEXT statement can also be used with multiple nested FOR/NEXT loops. The counter variables can be omitted but their use is recommended for clarity. For example:

FOR n = 1 TO 10

FOR m = 1 TO 20

FOR q = 1 TO 100

.

.

.

NEXT q,m,n ' Can also use: NEXT ,,

If a NEXT is encountered without a corresponding FOR (or vice versa), a compile-time error is generated.

If the Counter variable itself is never referenced by BASIC statements inside of the loop, and the start, stop and increment values are all expressed as numeric literals, PowerBASIC will optimize the loop to count "down" to zero for the specified number of iterations. Therefore, the Counter variable may equal start - increment when the loop ends:

FOR n& = 1 TO 100

NEXT n&

DispMsg n& ' may be 0 or 101

Although the compiler does not care about such things, it is considered good programming practice to indent the statements between FOR and NEXT by two or three spaces to set off the structure of the loop.

For additional performance, use a REGISTER variable for the loop counter variable.

Restrictions

A variable that has been passed by reference ( ) to a Sub or Function may not be used as a Counter. Use a local variable instead.

See also

#REGISTER, DO/LOOP, EXIT, ITERATE, WHILE/WEND, REGISTER


FORMAT$ function

Purpose

Format numeric data according to instructions contained in a format expression.

Syntax

x$ = FORMAT$(num_expression [, [digits& | fmt$]])

Remarks

FORMAT$ has the following parts:

num_expression

The numeric expression, variable, or literal value to be formatted. This argument is converted to full (Extended) precision before formatting commences.

digits&

The maximum number of significant digits, in the range of 1 to 18. If not included, PowerBASIC supplies a default value of 7 for single precision values, or 16 for more precise values. This form of the function is very similar to the STR$() function, except that it never supplies any leading or trailing spaces. Use care that digits& is large enough to contain the whole part of a number, or scientific notation must be used to estimate it. For example, FORMAT$(123.456, 2) returns the "1.2E+2", while FORMAT$(123.456, 5) returns the string "123.45".

fmt$

Format characters that will determine how the numeric expression should be formatted. This expression is termed the mask. There may be up to 18 digit-formatting digits on either side of the decimal point. The mask may not contain literal characters unless each character is preceded with a backslash (\) escape character, or the literal characters are enclosed in quotes.

fmt$ may contain one, two or three formatting masks, separated by semicolon (;) characters:

One mask | If fmt$ contains just one format mask, the mask is used to format all possible values of num_expression. For example:
 | x$ = FORMAT$(z!, "000.00")
Two masks | If fmt$ contains two format masks, the first mask is used for positive values (=> 0), and the second mask is used for negative values (< 0). For example:
 | x$ = FORMAT$(-100, "+00000.00;-000")
Three masks | If fmt$ contains three masks, the first mask is used for positive values (> 0), the second mask for negative values (< 0), and the third mask is used if num_expression is zero (0). For example:
 | FOR y! = -0.5! TO 0.5! STEP 0.5! x$ = FORMAT$(y!, "+.0;-.0; .0") NEXT y!

Digit placeholders in a mask do not have to be contiguous. This allows you to format a single number into multiple displayed parts. For example:

A$ = FORMAT$(123456, "00\:00\:00") ' 12:34:56

The following table shows the characters you can use to create the user-defined format strings (masks) and the definition of each formatting character:

Character

Definition

Empty string

[null string] No formatting takes place. The number is converted to Extended-precision and formatted similarly to STR$, but without the leading space that STR$ applies to non-negative numbers.

A$=FORMAT$(0.2) ' .200000002980232

A$=FORMAT$(0.2!, "") ' .200000002980232

A$=FORMAT$(0.2#) ' .2

A$=FORMAT$(0.2#, "") ' .2

0

[zero] Digit placeholder. PowerBASIC will insert a digit or 0 in that position.

If there is a digit in num_expression in the position where the 0 appears in the format string, return that digit. Otherwise, return "0". If the number being formatted has fewer digits than there are zeros (on either side of the decimal point) in the format expression, leading or trailing zeros are added. If the number has more digits to the right of the decimal point than there are zeros to the right of the decimal point in the format expression, the number is rounded to as many decimal places as there are zeros in the mask.

If the number has more digits to the left of the decimal point than there are zeros to the left of the decimal point in the format expression, the extra digits are displayed without truncation. If the numeric value is negative, the negation symbol will be treated as a decimal digit. Therefore, care should be exercised when displaying negative values with this placeholder style. In such cases, it is recommended that multiple masks be used.

' Numeric padded with leading zero characters

A$ = FORMAT$(999%, "00000000") ' 00000999

#

[Number symbol] Digit placeholder. If there is a digit for this position, PowerBASIC replaces this placeholder with a digit, nothing, or a user-specified character.

Unlike the 0 digit placeholder, if the numeric value has the fewer digits than there are # characters on either side of the decimal placeholder, PowerBASIC will either:

a) Omit this character position from the final formatted string; or

Substitute a user-specified replacement character if one has been defined (see the asterisk (*) character for more information). To specify leading spaces, prefix the mask with "* " (asterisk and a space character).

For example:

' No leading spaces and trailing spaces

A$ = FORMAT$(0.75!, "####.###") ' 0.75

' Up to 3 Leading spaces before decimal

A$ = FORMAT$(0.75!, "* ##.###") ' 0.75

' Using asterisks for padding characters

A$ = FORMAT$(0.75!, "*=##.###") ' ===0.75=

FORMAT$ may also return a string that is larger than the number of characters in the mask:

A$ = FORMAT$(999999.9, "#.#") ' 999999.9

.

[period] Decimal placeholder. Determines the position of the decimal point in the resultant formatted string.

If any numeric field is specified to the left of the decimal point, at least one digit will always result, even if only a zero. The zero is not considered to be a "leading" zero if it is the only digit to the left of the decimal. Placing more than one period character in the fmt$ string will produce undefined results.

%

[percent] Percentage placeholder. PowerBASIC multiplies num_expression by 100, and adds a trailing percent symbol. For example:

x$ = FORMAT$(1 / 5!, "0.0%") ' 20.0%

,

[comma] Thousand separator. Used to separate thousands from hundreds within a number that has four or more digits to the left of the decimal point. In order to be recognized as a format character, the comma must be placed immediately after a digit placeholder character (also see Restrictions below).

A$ = FORMAT$(1234567@, "#,") ' 1,234,567

A$ = FORMAT$(12345@, "#,.00") ' 12,345.00

A$ = FORMAT$(12345@, "#.00,") ' 12,345.00

A$ = FORMAT$(1212.46, "$00,000.00") ' $01,212.46

A$ = FORMAT$(1000%, """#""#,") ' #1,000

A$ = FORMAT$(1234567@, "0,") ' 1,234,567

*x

[asterisk] Digit placeholder and fill-character. Instructs PowerBASIC to insert a digit or character "x" in that position. If there is a digit in num_expression at the position where the * appears in the format string, that digit is used; otherwise, the "x" character is used (where "x" represents your own choice of character). The *x specifier acts as two digit (#) fields.

A$ = FORMAT$(9999.9@,"$**####,.00")' $**9,999.90

A$ = FORMAT$(0@,"$*=###0,.00#") ' $=====0.00=

A$ = FORMAT$(0@,"$* ####0,.00") ' $ 0.00

E- e- E+ e+

[e] Scientific format. PowerBASIC will use scientific notation in the formatted output. Use E- or e- to place a minus sign in front of negative exponents. Use E+ or e+ to place a minus sign in front of negative exponents and a plus sign in front of non-negative exponents.

In order to be recognized as a format sequence, the E-, e-, E+, or e+ must be placed between two digit placeholder characters. For example:

A$ = FORMAT$( 99.999, "0.0E-##") ' 1.0E2

A$ = FORMAT$(-99.999, "0.0E-##") ' -1.0E2

A$ = FORMAT$( 99.999, "0.0E+##") ' 1.0E+2

A$ = FORMAT$(-99.999, "0.0E+##") ' -1.0E+2

A$ = FORMAT$(0.1!, "0.0e+##") ' 1.0e-1

"

[double-quote] Quoted string. PowerBASIC treats all characters up to the next quotation mark as-is, without interpreting them as digit placeholders or format characters. Also see backslash. For example:

A$ = FORMAT$(12, $DQ+"##"+$DQ+"##") ' ##12

A$ = FORMAT$(5.55, """XYZ=""#.##\#") ' XYZ=5.55#

A$ = FORMAT$(25, """x=""#") ' x=25

A$ = FORMAT$(999, """Total ""#") ' Total 999

A$ = FORMAT$(5, $DQ+"x="+$DQ+"#") ' x=5

\x

[backslash] Escaped character prefix. PowerBASIC treats the character "x" immediately following the backslash (\) as a literal character rather than a digit placeholder or a formatting character. Many characters in a mask have a special meaning and cannot be used as literal characters unless they are preceded by a backslash.

The backslash itself is not copied. To display a backslash, use two backslashes (\\). To display a literal double-quote, use two double-quote characters.

To simplify the mask string for common numeric formats, FORMAT$ permits the dollar symbol, the left and right parenthesis symbols, the plus and minus symbols, and the space character ("$()+- ") to pass through from the mask string into the formatted output string, without requiring an escape (\) prefix character.

A$ = FORMAT$(23, "(* 0\%)") ' ( 23%)

A$ = FORMAT$(99999, "#\#") ' 99999#

A$ = FORMAT$(5, "\"+$DQ+$DQ+"x="+$DQ+ _

"#\"+$DQ) ' "x=5"

A$ = FORMAT$(5, "\""""x=""#\""") ' "x=5"

A$ = FORMAT$(1000%, """#####,""") ' "#####,"

Restrictions

You cannot pass a string expression or string variable in num_expression. Do not place more than one decimal point in the mask.

FORMAT$ can return the maximum possible number of digits (up to 4932 for Extended-precision); however, the resulting digits will be meaningless beyond the actual precision of num_expression. Consequently, the value of num_expression may produce formatted strings that are wider than the length of fmt$, for example:

A$ = FORMAT$(3e30!, "#,###") ' returns 41 characters

Rounding, if necessary, is implemented by the "banker's rounding" principle: if the fractional digit being rounded off is exactly five, with no trailing digits, the number is rounded to the nearest even number. This provides better results, on average, and follows the IEEE standard. For example:

A$ = FORMAT$(0.5##, "0") ' 0

A$ = FORMAT$(1.5##, "0") ' 2

A$ = FORMAT$(2.5##, "0") ' 2

A$ = FORMAT$(2.51##, "0") ' 3

Semicolon characters, being mask delimiters, should not be used for other purposes in mask strings unless prefixed with an escaped character symbol (\).

FORMAT$, when used with some formatting characters such as the thousands separator (comma), may not produce a "right-justified" formatted string. The simple solution is to apply separate justification with the RSET statement or the RSET$ function. For example:

A$ = SPACE$(12)

RSET A$ = FORMAT$(1,"#,###.00") ' 1.00

RSET A$ = FORMAT$(1000, "#,.00") ' 1,000.00

RSET A$ = FORMAT$(1000000,"#,###.00")' 1,000,000.00

B$ = RSET$(FORMAT$(1e6, "#,"),10) ' " 1,000,000"

One further enhancement would be to combine this into a MACRO function, for example:

MACRO mMoney1(d,l) = "$"+RSET$(FORMAT$(d,"#,"),l-1)

MACRO mMoney2(d,l) = RSET$(FORMAT$(d,"$#,"),l)

...

A$ = mMoney1(1000,10) ' "$ 1,000"

B$ = mMoney2(1000,10) ' " $1,000"

See also

BIN$, GUID$, HEX$, OCT$, REPEAT$, SPACE$, STR$, STRING$, USING$, VAL


FRAC function

Purpose

Return the fractional part of a number.

Syntax

h = FRAC(float_expression)

Remarks

FRAC returns the number after the decimal point of a floating-point number or expression. FRAC rounds the result of fit the precision of the target h, as per IEEE specifications.

See also

CEIL, CINT, FIX, INT, ROUND

Example

h# = FRAC(10.25#) ' = 0.25# (Double-precision)


FREEFILE function

Purpose

Return the next available PowerBASIC file number.

Syntax

x% = FREEFILE

Remarks

FREEFILE returns an Integer value in the range 1 to 32767, which dictates the next available file number that may be used to OPEN a file or device. Using FREEFILE, your program can open files and devices without the need to keep track of which file numbers are already in use.

FREEFILE is thread-safe, returning a new file number with each invocation. This means that two or more consecutive calls to FREEFILE will return different file numbers regardless of whether they were used to open a file or not. This behavior differs from previous versions of PowerBASIC, where FREEFILE returned the same file number consistently until the file number was actually used to open a file or device.

FREEFILE is vastly superior to using hard-coded file numbers because it eliminates the possibility of a file number being used more than once in a module at any given moment.

A file number returned by FREEFILE can be used with the COMM OPEN, TCP OPEN and UDP OPEN statements, as well as standard file I/O OPEN and OPEN HANDLE statements.

Use FILENAME$ to return the name of the open file that corresponds to a given file number.

Restrictions

FREEFILE returns file numbers within a predictable and convenient range of values. PowerBASIC file numbers are also specific (private) to the PowerBASIC module in which they are used to OPEN a file or device. This means that a file number in use in one module will have no definition or meaning if passed to another module or API function.

However, it can sometimes be necessary for a different module or even an API function to access a file that is already open. In this case, it is necessary to use the FILEATTR function to obtain the operating system file handle, and this value can be passed to other modules or API functions. These modules would use the OPEN HANDLE statement to gain access into the already-open file.

See also

COMM OPEN, FILEATTR, FILENAME$, OPEN, TCP OPEN, UDP OPEN

Example

x%= FREEFILE

OPEN MyFileName$ FOR OUTPUT AS #x%


FUNCNAME$ function

Purpose

Return the name of the current Sub/Function.

Syntax

f$ = FUNCNAME$

Remarks

FUNCNAME$ returns the name of the Sub/Function in which it is located. If an is specified, FUNCNAME$ returns the ALIAS name; otherwise, it returns the primary name capitalized. Returning the ALIAS name provides a mechanism to disguise sensitive internal Sub/Function names even when reporting error conditions to a user.

FUNCNAME$ can be useful as a debugging tool, or in situations where an error handler in a Sub/Function passes error information on to a "central" Sub/Function for logging and handling. FUNCNAME$ does not require #TOOLS ON.

See also

#TOOLS, CALLSTK, CALLSTK$, CALLSTKCOUNT, FILENAME$, FUNCTION/END FUNCTION, PROFILE, SUB/END SUB, TRACE

Example

SUB SecretEncryptionSub ALIAS "MySub" (sData$)

x$ = FUNCNAME$ ' Returns "MySub"

END SUB

...

SUB SecretDecryptionSub (sData$)

x$ = FUNCNAME$ ' Returns "SECRETDECRYPTIONSUB"

END SUB


FUNCTION/END FUNCTION statements

Purpose

Define a Function block.

Syntax

[STATIC] FUNCTION FuncName [BDECL | CDECL | SDECL] [ALIAS "AliasName"] [([arguments])] [EXPORT | PRIVATE] [STATIC] [AS type]

[LOCAL variable list]

[STATIC variable list]

[DIM variable list]

{statements}

[{FuncName | FUNCTION} = ReturnValue]

{statements}

[EXIT FUNCTION]

{statements}

END FUNCTION

Remarks

All executable code must reside in a Sub or Function block. You cannot define a Function (or Sub) inside another Function or Sub. The FUNCTION statement has the following parts:

STATIC

Specifies the default storage class for variables declared inside the Function If not specified, the default storage class is LOCAL. The STATIC keyword may appear before the FUNCTION keyword, or after the list of arguments.

FuncName

The name of the Function. A type-specifier may be appended (just like an ordinary variable name) to specify the data type of the Function's return value, in place of the [AS type] clause. FuncName must be unique: no other variable, Function, Sub, or label can share it. Also see ALIAS below.

Future versions of PowerBASIC may not support type-specifier symbols for the Function return type, so specify the return data type with an explicit AS type clause in all DECLARE and FUNCTION definitions, to ensure future compatibility.

BDECL

Specifies that the declared procedure uses the BASIC/Pascal calling convention. When a procedure calls a BDECL procedure, it passes its parameters on the stack from left to right.

It is the responsibility of the called procedure to clean up the stack before returning to the calling procedure. Therefore, all PowerBASIC Subs and Functions that specify the BDECL convention automatically clean up the stack before execution returns to the calling code.

CDECL

Specifies that the declared Function uses the C calling convention. When a CDECL Function is called, it passes its parameters on the stack from right to left.

The calling procedure removes any passed parameters from the stack as part of the return process. When PowerBASIC code calls Subs and Functions using the CDECL convention, the stack is cleaned automatically after execution returns from the called code.

In the event the called procedure is imported or exported, PowerBASIC will automatically create a C style ALIAS for the function name. This alias will be prefixed with an underscore followed by the original function name converted to lowercase.

The following two declarations are equivalent, indicating how the default ALIAS name would be created by PowerBASIC:

FUNCTION C_Function CDECL () EXPORT AS LONG

FUNCTION C_Function CDECL ALIAS "_c_function" () EXPORT AS LONG

SDECL

This is the default if neither BDECL nor CDECL are specified. SDECL (and its synonym STDCALL) specifies that the declared Function uses the "Standard Calling Convention" as defined by Microsoft. When calling an SDECL Function, parameters are passed on the stack from right to left.

PowerBASIC Subs and Functions that use the SDECL/STDCALL convention automatically clean up the stack before execution returns to the calling code.

ALIAS

String literal that identifies an case-sensitive alternative name for the procedure. This lets you export a Function (or Sub) by a name other than what it is called and referenced within the source code.

This can be useful if you want to abbreviate a long name, provide a more descriptive name, or if the exported name needs to contain characters that are illegal in PowerBASIC. AliasName is the routine's actual name as it appears in the export table, and FuncName is the title that you can use in PowerBASIC. For example:

FUNCTION ShortName ALIAS "LongFuncName"() EXPORT STATIC AS LONG

The ALIAS clause is very important when exporting Subs and Functions. Omitting the ALIAS clause or incorrectly capitalizing the alias name are common causes of "Missing Export" errors. Please refer to the DECLARE section for more information.

Passing parameters

arguments

An optional, comma-delimited sequence of formal parameters. The parameters used in the arguments list serve only to define the Function; they have no relationship to other variables in the calling code with the same name. The default variable scope within the Function body can be specified by including a type class keyword (STATIC, or LOCAL) at the end of the header; the default, if no keyword is included, is LOCAL. Also see STATIC (above).

Normally, PowerBASIC passes parameters to a Function either by reference (BYREF) or by copy (BYCOPY). Either way, the address of the variable is passed and the procedure has to look at that address to get the value of the parameter. If you do not need to modify the parameters (true in many cases), you can speed up your calls by passing the parameters by value using the BYVAL keyword.

You can also force a parameter to be passed by reference (rather than by value or by copy) by using the BYREF keyword.

The type of the parameter is specified either by appending a type-specifier character to the name or by using an AS clause. For example:

FUNCTION Test&(A AS INTEGER) 'integer passed by ref

FUNCTION Test&(A%) 'integer passed by ref

FUNCTION Test&(BYREF A%) 'integer passed by ref

FUNCTION Test&(BYVAL A%) 'integer passed by val

Parameter restrictions

PowerBASIC compilers have a limit of 32 parameters per FUNCTION. To pass more than 32 parameters to a FUNCTION, construct a User-Defined Type (UDT) and pass the UDT by reference (BYREF) instead.

Fixed-length strings, ASCIIZ strings, and User-Defined Types/Unions may also be passed as BYVAL or OPTIONAL parameters, now. Try to avoid passing large items BYVAL, as it’s terribly inefficient, and there is a maximum size limit of 64 Kb for a given parameter list.

PowerBASIC Functions cannot return an array, Variant, or Object variable as a Function return value. Pass these variable types as BYREF parameters instead. For example:

lResult& = ProcessData(TheArray&(), iSize%)

...

FUNCTION ProcessData(lArr() AS LONG, iSize%) AS LONG

REDIM lArr(iSize%) AS LONG

lArr(iSize%) = 1&

FUNCTION = -1&

END FUNCTION

Pointer parameters

When a Function definition specifies either a BYREF parameter or a variable parameter, the calling code may freely pass a BYVAL DWORD or a Pointer instead. While the use of the explicit BYVAL override in the calling code is optional, it is recommended for clarity.

The AS clause is required when declaring a pointer as a parameter. Each pointer parameter must be declared to be passed BYVAL. The use of the BYVAL keyword in the calling code is optional but recommended for clarity:

' Integer Pointer (passed by value)

FUNCTION Test(BYVAL A AS INTEGER PTR) AS LONG

This allows the parameter to be accessed indirectly using the at sign '@' de-referencing prefix as in:

@A = 56

Additional information on BYVAL/BYREF/BYCOPY parameter passing can be found in the CALL statement topic.

Optional parameters

PowerBASIC now supports two syntax formats for optional parameters: the classic optional parameter syntax using brackets "[..]", and the new syntax using the OPTIONAL (or OPT) keyword. We'll discuss each one in turn.

Using OPTIONAL/OPT

FUNCTION statements may specify one or more parameters as optional by preceding the parameter with either the keyword OPTIONAL (or the abbreviation OPT). Optional parameters are only allowed with CDECL or SDECL calling conventions, not BDECL.

When a parameter is declared optional, all subsequent parameters in the declaration are optional as well, whether or not they specify an explicit OPTIONAL or OPT directive. The following two lines are equivalent, with both second and third parameters being optional:

FUNCTION sABC(a&, OPTIONAL BYVAL b&, OPTIONAL BYVAL c&) AS LONG

FUNCTION sABC(a&, OPT BYVAL b&, BYVAL c&) AS LONG

When optional parameters are omitted in the calling code, the stack area normally reserved for those parameters is zero-filled. This allows you to test if an optional parameter was passed or not:

If the parameter is defined as a BYVAL parameter, it will have the value zero. For TYPE or UNION variables passed BYVAL, the compiler will pass a string of binary zeroes of length SIZEOF(Type_or_union_var).

If the parameter is defined as a BYREF parameter, VARPTR (varname) will equal zero; when this is true, any attempt to use Var_name in your code will result in Error #9 (null pointer); failure to detect this error using error-trapping may result in a General Protection Fault or memory corruption.

The OPTIONAL directive provides the same functionality as the older syntax using square brackets "[..]". See below.

Using classic optional parameters

When declaring a CDECL SUB or FUNCTION, you can specify trailing parameters as optional, using a set of brackets [..]:

FUNCTION KerPlunk CDECL (x%, y% [, z%]) AS LONG

Note that the comma separating the y% parameter from the optional z% parameter is inside the brackets. The following calling sequences would then be valid:

x& = KerPlunk(x%, y%)

x& = KerPlunk(x%, y%, z%)

Optional parameters must be the last parameters designated in the list. The following is invalid:

FUNCTION KerPlunk CDECL ([x%,] y%, z%) AS LONG

Because the FUNCTION (or SUB) being called does not know how many parameters are being passed at the time it is called, you should pass the number of parameters as one of the required parameters in the list.

PowerBASIC continues to support the use of classic optional parameter syntax using brackets ([..]) but this will not be the case in future versions of PowerBASIC. Existing code should be changed to the new OPTIONAL syntax as soon as possible to ensure compatibility with future versions of PowerBASIC.

EXPORT

Functions are private by default. The EXPORT keyword can be used to make a Function accessible from another module. Exported Subs and Functions can only be imported from a DLL, not from another .EXE.

PRIVATE

Functions are private by default. The PRIVATE keyword is not compulsory, but may be used to help make your code more easily read.

AS type

You may specify the type of data returned by a Function to the calling code. If you do not specify a type, PowerBASIC assumes that the Function returns the data type specified by a DEFtype statement. However, if no DEFtype or AS type has been specified, a compile-time error is generated.

Therefore, there are two ways to specify the return type of a Function:

· Include a type-specifier character at the end of FuncName

· Include the AS type clause as the last part of the FUNCTION statement (this is the recommended syntax to ensure compatibility with future versions of PowerBASIC).

For example, the following statements are equivalent:

FUNCTION aFunction?()

FUNCTION aFunction() AS BYTE

Assigning a return value

You can specify the return value of the Function by explicitly setting the value, either by assigning a value to the FUNCTION keyword, or by assigning a value to the function name. For example, the two lines within the following Function block are equivalent:

FUNCTION AddData() AS LONG

...

AddData = 123& ' Assign value to function name

FUNCTION = 123& ' Assign value to the function

END FUNCTION

Default return value

If the code within the Function does not explicitly set a return value, the default return value will be zero if the function returns a numeric data type, or an empty string if the function returns a string. For example:

FUNCTION AddData() AS LONG

...

IF condition THEN

EXIT FUNCTION ' No assignment, will return 0&

ELSE

FUNCTION = -1& ' An explicit return value

END IF

END FUNCTION

PowerBASIC Functions cannot return an array as a Function return value. Pass the array as a parameter instead. For example:

lResult& = CheckTheData(InTheArray&())

...

FUNCTION CheckTheData(lArr() AS LONG) AS LONG

...

END FUNCTION

Variables within functions

The default scope for variables within Functions is LOCAL. Placing the STATIC keyword before the FUNCTION keyword forces all variables inside the Function to default to STATIC scope.

Function blocks are constructed very much like Subs (see SUB/END SUB statement). However, Functions differ from Subs in that they can be used in expressions, and they return a value simply by assigning it to the Function name (or with the FUNCTION = statement; see above).

Functions cannot return fixed-length strings, structures, or whole arrays. You may return a pointer to these data types, however. If doing so, the target variable should be declared as STATIC or GLOBAL.

LOCAL variables are created within the Sub/Function's stack frame. If a LOCAL variable exceeds the amount of stack space available, it may become necessary to use a STATIC or GLOBAL variable instead. For example, creating a LOCAL ASCIIZ or LOCAL fixed-length string that is very large (say, approaching 1 MB) can trigger a General Protection Fault (GPF) because it may overrun the stack frame.

See also

DECLARE, EXIT, FUNCNAME$, GLOBAL, LOCAL, STATIC, SUB/END SUB, THREAD CREATE, THREADID

Example

FUNCTION HalfOf ALIAS "HalfOf" (X!) EXPORT AS SINGLE

FUNCTION = X! / 2

END FUNCTION


GET statement

Purpose

Read a record from a random-access file, or a variable or an array from a binary file.

Syntax

Random-Access files:

GET [#] filenum&, [Rec], [ABS] Var

GET [#] filenum& [, Rec]

Binary files:

GET [#] filenum&, [RecPos], Var

GET [#] filenum&, [RecPos], Arr() [RECORDS rcds] [TO count]

Remarks

The GET statement has the following parts:

filenum&

The file number under which the file was opened.

Random Access files

Rec

For random-access files, Rec is the record number to be read, from 1 to 2^63-1 (the maximum positive value for a Quad-integer). If Rec is omitted, the next record in sequence (following the one specified by the most recent GET or PUT) is read. If the file was just opened, the first record is read.

Var

If Var is smaller than the defined record length, GET will read enough data to fill Var. The remainder of the record is discarded and the file pointer is placed at the next record position. If Var is larger than the defined record length, GET will read one record into Var, and the file pointer will be moved to the next record.

When GET is used to retrieve data from a random access file into a dynamic (variable-length) string, PowerBASIC looks for a 2-byte (WORD) length field at the beginning of each record which indicates the length of the string that follows. The length of the string is set by this value. The argument to the LEN clause of the OPEN statement must be at least 2 bytes greater than the actual string length. This 2-byte (WORD) length field is placed in the file by the PUT statement when used with dynamic (variable-length) strings.

(no Var) When the second form of GET is used (without a Var target string), GET reads the file data from the current file pointer into an internal buffer. This data can then be accessed using FIELD string variables.

ABS

When GET is used to read a random file into a dynamic string, it normally expects the first two bytes of the record to contain the length of the valid data contained in the record. The ABS keyword specifies that no length word exists in the data, and the number of bytes to read is defined by the current length of the dynamic string variable. If the variable length is greater than the file record length, the remainder of the string variable is filled with nul's (CHR$(0) or $NUL). This offers greater compatibility with the actual operation of other versions of BASIC, such as PowerBASIC for DOS.

A random access file record is limited to 32768 bytes to ensure consistent behavior across all supported Win32 platforms. GET$ and other related functions are not constrained in this manner.

Binary files

RecPos

For binary files, RecPos is the starting byte or from where the GET should begin. The optional BASE = clause of the OPEN statement defines whether the first position is 0 or 1. The base position is 1 by default. If RecPos is greater than the number of records or bytes in the file, no error occurs but unpredictable data may be read. Use the EOF function to avoid reading past the end of the file.

Var

When used with a binary file, GET retrieves enough data from the file to fill Var. The Var parameter can be a simple (scalar) variable like an Integer or a dynamic (variable-length) string, an element in an array, or a variable of User-Defined Type.

Arr()

When reading an array from the disk file, GET assigns data from the file into each element in the array, starting at the arrays LBOUND subscript. GET attempts to read the number of elements specified by rcds in the RECORDS option, or the number of elements in the array, whichever is smaller. The actual number of elements read is assigned to the variable count specified in the optional TO clause.

With a dynamic string array, it is assumed the file was written in the PowerBASIC and/or VB packed string format using PUT of an entire string array. If a string is shorter than 65535 bytes, a 2-byte length WORD is followed by the string data. Otherwise, a 2-byte value of 65535 is followed by a length Double-word (DWORD), then finally the string data.

With other array arrays types, the entire data area is read as a single block. In either case, it is presumed the file was created with the complementary PUT Array statement.

EOF is set just as with other GET statements.

You can use the FILESCAN statement to determine the number of records contained in the file, allowing an array of the appropriate type to be dimensioned before using the GET statement to read the file.

See also

CSET, CSET$, EOF, FIELD, FILESCAN, GET$, LINE INPUT#, LSET, OPEN, PRINT#, PUT, PUT$, RSET, TYPE, SEEK, WRITE#

Example

' Random-access GET example

DIM uName AS STRING * 20

DIM I AS QUAD

DIM F AS LONG

F = FREEFILE

OPEN "TESTFILE.DTA" FOR RANDOM AS #F LEN = LEN(uName)

WHILE uName <> SPACE$(20)

PUT #F,, uName

uName = GetInput()

WEND

IF SEEK(F) > 0 THEN

ShowText "The file contains these names:"

FOR ix = 1 TO SEEK(F)

GET #F, ix, uName

ShowText uName + NL

NEXT

ELSE

ShowText "The file is empty"

END IF

CLOSE #F

' Binary GET Array example

OPEN "Data file to read.dat" FOR BINARY AS #1

FILESCAN #1, RECORDS TO count&

DIM TheData$(1 TO count&)

GET #1, 1, TheData$() TO y&

CLOSE #1


GET$ statement

Purpose

Read a from a file opened in binary mode.

Syntax

GET$ [#] filenum&, Count&, sVar$

Remarks

The GET$ statement has the following parts:

filenum&

Is the file number under which the file was opened.

Count&

Specifies how many bytes to read.

sVar$

The string variable to read the data into.

Remarks

GET$ reads Count& characters from file number filenum&, and assigns them to sVar$. File filenum& must have been opened in binary mode. Characters are read starting at the current file pointer position, which can be set with the SEEK statement. When the file is first opened, the pointer is at the beginning of the file (unless the LEN clause is used in the corresponding OPEN statement, position 1 is used by default). After GET$, the file pointer position will have been advanced by Count& bytes.

GET$, PUT$, and SEEK provide a low-level alternative to sequential and random-access file-processing techniques, allowing you to deal with files on a byte-by-byte basis.

See also

EOF, GET, INPUT#, LINE INPUT#, LOF, OPEN, PRINT#, PUT, PUT$, SEEK, WRITE#

Example

' Open binary file, write the alphabet A-Z to it

OPEN "SEEK.DTA" FOR BINARY AS #1 LEN = 1 BASE = 0

FOR I& = 65 TO 90

PUT$ #1, CHR$(I&)

NEXT I&

' Now read five characters at a time from the file,

' starting at different pointer positions

FOR I& = 0 TO 20 STEP 5

SEEK #1, I&

GET$ #1, 5, TempString$

x$ = "Starting at position" + STR$(I&) + $SPC + $DQ + TempString$ + $DQ

NEXT I&

CLOSE #1

Result Starting at

Result

Starting at position 0 "ABCDE"

Starting at position 5 "FGHIJ"

Starting at position 10 "KLMNO"

Starting at position 15 "PQRST"

Starting at position 20 "UVWXY"


GETATTR function

Purpose

Return the file-system attribute(s) of a disk file or directory.

Syntax

x& = GETATTR(filespec$)

Remarks

filespec$ specifies a filename or directory (optionally, including a drive letter and directory path). The attribute code returned in x& is a standard operating system attribute code, or a combination of several codes ORed together:

Attribute | Description | Equate*
0 | Normal** | %NORMAL
1 | Read-only | %READONLY
2 | Hidden | %HIDDEN
4 | System | %SYSTEM
8 | Volume Label | %VLABEL
16 | Directory | %SUBDIR
32 | Archived | %ARCHIVE
128 | Normal** | (synonym of %NORMAL)

* These equates can be found in the WIN32API.INC text file

** Some operating systems may return either 0 or 128 for normal files.

If GETATTR returns an attribute of 0 (or 128), filespec$ is a regular file: not read-only, not hidden, not system, and not archived.

Additional file attributes may be supported on some file systems. See the %FILE_ATTRIBUTE equates in your Win32API.inc file for a full list.

If you want to test for a single attribute, use the bitwise AND operator to strip out any other attributes that might be set. See the example below.

GETATTR can also be used to verify the existence of a file or directory, taking advantage of the fact that ERR will be set if the file/directory does not exist. See the example below.

Restrictions

If filespec$ cannot be found, a run-time Error 53 ("File not found") occurs. You cannot obtain the attributes of the root directory (i.e., "C:\"). Windows prevents this particular operation, triggering an Error 53.

See also

DIR$, FILEATTR, SETATTR

Example

' General GETATTR example

attr& = GETATTR("C:\CONFIG.SYS")

IF (attr& AND 32&) = 32& THEN

x$ = "CONFIG.SYS has been modified"

ELSE

x$ = "CONFIG.SYS hasn't been modified"

END IF

' Exist() function using GETATTR

FUNCTION Exist(File$) AS LONG

LOCAL Dummy&

Dummy& = GETATTR(File$)

FUNCTION = (ERRCLEAR = 0)

END FUNCTION


GETSTDERR function

Purpose | Return the handle of the initial standard error device.
Syntax | hStdErr& = GETSTDERR
Remarks | The standard error handle can be used with some Win32 API calls.
See also | CONSIN, CONSOUT, GETSTDIN, GETSTDKBD, GETSTDOUT, GETSTDVID, PRINT, STDEOF, STDERR, STDIN LINE, STDOUT


GETSTDIN function

Purpose | Return the handle of the initial standard input device.
Syntax | hStdIn& = GETSTDIN
Remarks | The standard input handle can be used with some Win32 API calls. It can also be used with the OPEN HANDLE statement, if you wish to treat the keyboard as a file. You should use CONSIN to determine if file or device redirection is in effect.
See also | CONSHNDL, CONSIN, CONSOUT, GETSTDERR, GETSTDKBD, GETSTDOUT, GETSTDVID, PRINT, STDEOF, STDERR, STDIN LINE, STDOUT


GETSTDKBD function

Purpose | Return the handle of the standard keyboard device.
Syntax | hKbd& = GETSTDKBD
Remarks | Redirection can cause the standard keyboard handle to be different from the handle returned by the GETSTDIN function. GETSTDKBD always returns the handle of the keyboard regardless of any redirection state.
See also | CONSHNDL, INKEY$, INSTAT, GETSTDERR, GETSTDIN, GETSTDOUT, GETSTDVID


GETSTDOUT function

Purpose | Return the handle of the initial standard output device.
Syntax | hStdOut& = GETSTDOUT
Remarks | The standard output handle can be used with some Win32 API calls. It can also be used with the OPEN HANDLE statement, if you wish to treat the screen as a file. You should use CONSOUT to determine if file or device redirection is in effect.
See also | CONSHNDL, CONSIN, CONSOUT, GETSTDERR, GETSTDIN, GETSTDKBD, GETSTDVID, PRINT, STDEOF, STDERR, STDIN LINE, STDOUT


GETSTDVID function

Purpose | Return the handle of a nominated console video page.
Syntax | hVid& = GETSTDVID(apage&)
Remarks | GETSTDVID returns the handle of the console video page given by the parameter apage&. apage& must be in the range 1 through 8. If apage& is omitted, or equal to zero, the handle of the current active page is returned.
See also | CONSHNDL, GETSTDERR, GETSTDKBD, GETSTDOUT, PAGE, PAGEACTIVE, PCOPY


GLOBAL statement

Purpose

Declare global (shared) variables between Subs and Functions.

Syntax

GLOBAL variable[()] [ AS type] [, variable[()]] [, ...]

GLOBAL variable[()] [, variable[()]] [, ...] AS type

Remarks

GLOBAL declares the specified variable(s) as global to the entire program. This gives a Sub or Function access to variable(s), without having to pass them as parameters. To declare an array as a global variable, use an empty set of parentheses in the variable list:

GLOBAL MyArray%()

GLOBAL StringArray() AS STRING

You must then use the DIM or REDIM statements to dimension the array inside a Sub or Function. A good place to do this is inside your WINMAIN or PBMAIN function.

If an array is defined as GLOBAL outside a Sub or Function, you should include the GLOBAL keyword in the DIM statement for clarity, and compatibility with future versions of PowerBASIC:

GLOBAL a() AS STRING

FUNCTION PBMAIN

DIM a(1 TO 500) AS GLOBAL STRING

...

END FUNCTION

The GLOBAL statement may accept a list of variables, all of which are defined by the type descriptor keywords which follow them. For example:

GLOBAL aaa, bbb, ccc AS INTEGER

GLOBAL vptr, aptr() AS LONG PTR

Restrictions

GLOBAL variables are not shared between programs and DLLs or between multiple instances of the same DLL. That is, a GLOBAL variable is only global within its own module. The simplest way to expose a variable to a DLL is to pass the variable (by reference) to the target DLL. DEFtype has no effect on variables defined by a GLOBAL statement.

See also

DIM, LOCAL, STATIC, THREADED

Example

#COMPILE EXE

GLOBAL Caption AS ASCIIZ * 255

FUNCTION PBMAIN() AS LONG

DIM Msg AS ASCIIZ * 255

CALL SetVars

IF Caption = "GLOBAL test" then Msg = "Success!"

END FUNCTION

SUB SetVars()

Caption = "GLOBAL test"

END SUB


GOSUB/GOSUB DWORD statements

Purpose

Invoke a subroutine.

Syntax

GOSUB {label | linenumber}

GOSUB DWORD dwpointer

Remarks

GOSUB causes execution to branch to the statement prefaced by label or linenumber, after first saving its current location on the stack. The label or linenumber must be local to the Sub or Function where the GOSUB statement is located. Executing a RETURN statement returns control to the instruction immediately following the GOSUB.

When using GOSUB, be sure that each subroutine returns to its caller gracefully through a RETURN statement. Run-away (recursive) GOSUBs that loop upon themselves will eat up large chunks of stack space, reducing available memory.

All labels and line numbers are private. You cannot GOSUB to a label outside of the current Sub or Function.

For time critical or high-performance code, using a GOSUB to perform a repetitive task is almost always faster then performing a call to a Sub or Function, since there is no overhead in setting up a stack frame for a GOSUB.

DWORD

GOSUB DWORD causes execution to branch to address stored in dwpointer, after first saving its current location on the stack. dwpointer must be a Double word, Long integer, or variable that contains the address of a label that is in the same Sub or Function as the GOSUB DWORD statement. Executing a RETURN statement returns control to the instruction immediately following the GOSUB DWORD statement.

See also

#STACK, FUNCTION/END FUNCTION, ON GOSUB, SUB/END SUB, RETURN

Example

FUNCTION DoCalc!(Radius!)

pi# = ATN(1) * 4 ' calculate value of PI

GOSUB CalcArea ' jump to subroutine Radius!

EXIT FUNCTION

CalcArea:

FUNCTION = pi# * Radius! ^2 ' calculate area

RETURN ' return from subroutine

END FUNCTION


GOTO/GOTO DWORD statements

Purpose

Transfer program execution to the statement identified by a label or line number.

Syntax

GOTO {label | linenumber}

GOTO DWORD dwpointer

Remarks

GOTO causes program flow to jump unconditionally to the code identified by label or linenumber. The label or linenumber must be local to the Sub or Function where the GOTO statement is located. GOTO differs from GOSUB and other similar control statements, in that after execution of a GOTO, the program retains no memory of where it was before it executed the jump.

Labels and line numbers are private. You cannot GOTO a label outside of the current Sub or Function.

DWORD

GOTO DWORD causes execution to jump unconditionally to address stored in dwpointer. dwpointer must be a Double word, Long integer, or variable that contains the address of a label which is local to the Sub or Function where the GOTO DWORD statement is located.

See also

CALL, CALL DWORD, DO/LOOP, EXIT, FOR/NEXT, FUNCTION/END FUNCTION, GOSUB, IF block, RETURN, SELECT, SUB/END SUB, WHILE/WEND

Example

FUNCTION test() AS LONG

RESET X

Start: ' define a label

INCR X ' increment X

IF X < 10 THEN DoBeep

EXIT FUNCTION

...

DoBeep:

BEEP

GOTO Start ' jump back to Start

END FUNCTION

One method of obtaining the same results without use of GOTO is:

FUNCTION test() AS LONG

FOR X = 1 TO 9

GOSUB DoBeep

NEXT X

EXIT FUNCTION

...

DoBeep:

BEEP

RETURN

END FUNCTION


GRAPHIC ARC statement

Purpose

Draw an arc in the selected graphic window.

Syntax

GRAPHIC ARC (x1!, y1!) - (x2!, y2!), arcStart!, arcEnd! [, rgbColor&]

Remarks

An arc is a section of a circle or an ellipse. To specify a particular arc, you would first define the full circle or ellipse of which it is a part, and then specify the points on the ellipse where the arc starts and stops.

The full circle or ellipse is defined by its bounding rectangle, which is defined as the smallest rectangle which can be drawn around the circle or ellipse. For example, if the circle is centered at position (400,400), with a radius of 100 pixels, the upper left corner (x1!, y1!) of the bounding rectangle is (300,300), and the lower right corner (x2!, y2!) is (500,500). The start point and end point of the arc are specified by their angle, which must be given in radians. A complete circle or ellipse is 2*pi radians. On a 12-hour clock-face, the values 0 and 2*pi both refer to the position of 3 o'clock, while the value 1*pi refers to the position of 9 o'clock. Other positions are specified by a radian value relative to these. In PowerBASIC, arcs are always drawn counter-clockwise from the starting point to the ending point.

Prior to any graphical operations, the graphic target must first be selected with GRAPHIC ATTACH. The Coordinates are specified in the same terms (pixels or dialog units) as the parent dialog (or world coordinates, if those were chosen with GRAPHIC SCALE). Line width can be set using GRAPHIC WIDTH. If line width is set to 1 (the default), the line style can be set with GRAPHIC STYLE. Because of the nature of an arc, GRAPHIC ARC neither uses, nor updates, graphic POS (last point referenced).

x1!, y1!

The upper left corner of the bounding rectangle of the full circle or ellipse.

x2!, y2!

The lower right corner of the bounding rectangle of the full circle or ellipse.

ArcStart!

The starting angle of the arc, in radians, from 0 to 2*pi.

ArcEnd!

The ending angle of the arc, in radians, from 0 to 2*pi radians. Note that arcs are always drawn counter-clockwise from arcStart! to arcEnd!. Compared with a 12-hour clock-face, 0 or 2*pi radians is at 3 o'clock, and 1*pi radians is at 9 o'clock.

rgbColor&

Optional RGB color for the arc. If omitted (or -1), the current foreground color for the graphic window is used.

See also

GRAPHIC ATTACH, GRAPHIC COLOR, GRAPHIC ELLIPSE, GRAPHIC PIE, GRAPHIC STYLE, GRAPHIC WIDTH

Example

' Draw two arcs that combine into a circle.

' The upper half uses the default foreground color.

' The lower half is drawn in red.

LOCAL Pi AS DOUBLE

Pi = 4 * ATN(1) ' Calculate Pi

GRAPHIC ARC (5, 5) - (105, 105), 0, Pi ' Upper half

GRAPHIC ARC (5, 5) - (105, 105), Pi, 0, %RED ' Lower half


GRAPHIC ATTACH statement

Purpose

Select the graphic target (window or ) on which future drawing operations will take place.

Syntax

GRAPHIC ATTACH hWin???, id& [, REDRAW]

Remarks

The selection remains in effect until another GRAPHIC ATTACH statement is executed, or the graphic target is deleted. All PowerBASIC graphical displays are persistent; they will be automatically redrawn even if minimized or temporarily covered by another window.

hWin???

graphic window or bitmap to be used with statements.

id&

Reserved for future implementation. Must be 0 for a graphic window or bitmap.

REDRAW

Optional parameter that causes all drawing statements to be buffered until a GRAPHIC REDRAW statement is executed, or the operating system chooses to update the target window. Without REDRAW, all graphical statements (Line, Box, etc.) are performed immediately. However, in certain intensive drawing operations, it may be preferable to delay the display until a number of statements have been performed. In some cases, this can improve the overall performance dramatically.

See also

GRAPHIC BITMAP LOAD, GRAPHIC BITMAP NEW, GRAPHIC WINDOW


GRAPHIC BITMAP END statement

Purpose | Close the selected graphic bitmap.
Syntax | GRAPHIC BITMAP END
Remarks | You must close every memory bitmap (that was created with GRAPHIC BITMAP LOAD or GRAPHIC BITMAP NEW) when you are finished using them for graphical operations. To close a bitmap, select it with the GRAPHIC ATTACH statement, then execute GRAPHIC BITMAP END.
See also | GRAPHIC ATTACH, GRAPHIC BITMAP END, GRAPHIC BITMAP LOAD, GRAPHIC BITMAP NEW


GRAPHIC BITMAP LOAD statement

Purpose | Create a memory bitmap and load an image into it.
Syntax | GRAPHIC BITMAP LOAD BitName$, nWidth&, nHeight& TO hBmp???
Remarks | GRAPHIC BITMAP LOAD creates a new memory bitmap, loading a bitmap image from a resource or a disk file. This bitmap works just like a GRAPHIC WINDOW, except that it is not visible. The parameter BitName$ specifies the name of the image to be loaded. If BitName$ contains a period, it is presumed to be the name of a disk file. Otherwise, an attempt is made to load it from a resource -- if not found, it is then presumed to be a disk file. The parameters nWidth& and nHeight& specify the width and height of the bitmap, in pixels. If the bitmap creation is successful, the bitmap handle is assigned to the variable hbmp???. If not successful, hbmp??? is set to zero. When you are finished using this memory bitmap, you must delete it with GRAPHIC BITMAP END. The following code will retrieve the natural size of an image in a bitmap file, in pixels: nFile& = FREEFILE OPEN "myimage.bmp" FOR BINARY AS nFile& GET #nFile&, 19, nWidth& GET #nFile&, 23, nHeight& CLOSE nFile&
BitName$ | The name of the bitmap image to load.
nWidth& | The width of the bitmap, in pixels.
nHeight& | The height of the bitmap, in pixels.
hBmp??? | The bitmap handle.
See also | GRAPHIC ATTACH, GRAPHIC BITMAP END, GRAPHIC BITMAP NEW, GRAPHIC WINDOW


GRAPHIC BITMAP NEW statement

Purpose | Create a new memory bitmap.
Syntax | GRAPHIC BITMAP NEW nWidth&, nHeight& TO hBmp???
Remarks | GRAPHIC BITMAP NEW creates a new memory bitmap, which may be manipulated and drawn just as if it were a GRAPHIC WINDOW, except that it is not visible. The parameters nWidth& and nHeight& specify the width and height of the bitmap, in pixels. If the bitmap creation is successful, the bitmap handle is assigned to the variable hBmp???. If not successful, hBmp??? is set to zero. When you are finished using this memory bitmap, you must delete it with GRAPHIC BITMAP END.
See also | GRAPHIC ATTACH, GRAPHIC BITMAP END, GRAPHIC BITMAP LOAD


GRAPHIC BOX statement

Purpose

Draw a box with square or rounded corners in the selected graphic window.

Syntax

GRAPHIC BOX (x1!, y1!) - (x2!, y2!) [, [corner&] [, [rgbColor&] [,

[fillcolor&] [, [fillstyle&]]]]]

Remarks

The graphic target must first be selected with GRAPHIC ATTACH. The Coordinates are specified in the same terms (pixels or dialog units) as the parent dialog (or world coordinates, if those were chosen with GRAPHIC SCALE). Line width can be set using GRAPHIC WIDTH. If line width is set to 1 (the default), the line style can be set with GRAPHIC STYLE. Because of the nature of an arc, GRAPHIC ARC neither uses, nor updates, the last point referenced (POS).

x1!, y1!

The upper left corner of the box.

x2!, y2!

The lower right corner of the box.

corner&

The percentage of roundness of the corners, in the range of 0 to 100. A value of zero creates square corners, while 100 creates a circle/oval. A value of 20 being most common for a pleasant, rounded appearance. If corner& is omitted, the default is 0, which creates a rectangle with square corners.

rgbColor&

Optional RGB color of the box edge. If omitted (or -1), the edge color defaults to the current foreground color for the selected graphic window.

fillcolor&

Optional RGB color of the box interior. If fillcolor& is omitted (or -2), the interior of the box is not filled, allowing the background to show through. If fillcolor& is -1, the interior is painted with the same color as the edge. Otherwise, fillcolor& specifies the RGB color to be used.

fillstyle&

Optional fill style (pattern) to be used. If fillstyle& is omitted, the default fill style is solid (0). If a hatch pattern is chosen (1 to 6), the foreground color is specified by the fillcolor&, while the background is specified by the default background color. The optional fillstyle& may be:

0 | Solid (default)
1 | Horizontal Lines
2 | Vertical Lines
3 | Upward Diagonal Lines
4 | Downward Diagonal Lines
5 | Crossed Lines
6 | Diagonal Crossed Lines

See also

GRAPHIC ATTACH, GRAPHIC COLOR, GRAPHIC LINE, GRAPHIC STYLE, GRAPHIC WIDTH

Example

' Draw rectangle with square corners and default colors.

GRAPHIC BOX (10, 10) - (100, 80)

' Draw a blue rectangle with 20% rounded corners,

' filled with a light-gray, diagonal cross pattern

GRAPHIC BOX (15, 15) - (95, 75), 20, %BLUE, RGB(191,191,191), 6


GRAPHIC CHR SIZE statement

Purpose

Retrieve the character size for the current font in the selected graphic window.

Syntax

GRAPHIC CHR SIZE TO ncWidth!, ncHeight!

Remarks

The graphic window must first be selected with GRAPHIC ATTACH. The character size is specified in the same terms (pixels or dialog units) as the parent dialog (or world coordinates, if those were chosen with GRAPHIC SCALE).

If the font is a fixed-width font, like Courier New or Lucida Console, the sizes returned are as exact as possible, given the rounding errors possible when converting from pixels to other coordinates. If the font is proportional, like Arial or Times New Roman, the width will be the average for the entire font.

See also

GRAPHIC ATTACH, GRAPHIC FONT, GRAPHIC PRINT, GRAPHIC TEXT SIZE


GRAPHIC CLEAR statement

Purpose

Clear the entire selected graphic window, optionally using a specified color and fill style.

Syntax

GRAPHIC CLEAR [rgbColor& [, fillstyle&]]

Remarks

The graphic window must first be selected with GRAPHIC ATTACH. The last point referenced (POS) is set to the upper left corner of the graphic window (0,0).

rgbColor&

Optional RGB value representing the fill color. If rgbColor& is omitted (or -1), the graphic window is cleared to the default background color for the selected graphic window.

fillstyle&

Optional fill style (pattern) to be used. If fillstyle& is omitted, the default fill style is solid (0). If a hatch pattern is chosen (1 to 6), the foreground color is specified by the rgbColor&, while the background is specified by the default background color for the selected graphic window. The optional fillstyle& may be:

0 | Solid (default)
1 | Horizontal Lines
2 | Vertical Lines
3 | Upward Diagonal Lines
4 | Downward Diagonal Lines
5 | Crossed Lines
6 | Diagonal Crossed Lines

See also

GRAPHIC ATTACH


GRAPHIC COLOR statement

Purpose

Set the foreground color and optionally the background color for various statements.

Syntax

GRAPHIC COLOR foreground& [, background&]

Remarks

The graphic window must first be selected with GRAPHIC ATTACH. If either parameter is -1, the default foreground/background color is used. If the background parameter is -2, the background is not painted, allowing the content behind to become visible. Otherwise, the specified RGB color is used.

See also

GRAPHIC ATTACH, GRAPHIC PRINT

Example

' Set red foreground and blue background color.

GRAPHIC COLOR %RED, RGB(0,0,191)


GRAPHIC COPY statement

Purpose

Copy a to the selected graphic target.

Syntax

GRAPHIC COPY hbmpSource???, id& [, style&]

GRAPHIC COPY hbmpSource???, id& TO (x!, y!) [, style&]

GRAPHIC COPY hbmpSource???, id&, (x1!, y1!)-(x2!, y2!) TO (x!, y!) [, style%]

Remarks

You can copy a complete bitmap, or a portion of it, to the selected graphic target. The expression hbmpSource??? specifies the handle of the source bitmap or window. The expression id& is reserved for future use, and must always be set to zero. The destination of the copy operation is the window selected by GRAPHIC ATTACH. You must take care that your parameters are valid for the specified bitmap, or the results of the operation are undefined.

The first form of the GRAPHIC COPY statement copies the complete bitmap, positioning it at (0,0), which is the upper left corner of the destination.

The second form of GRAPHIC COPY also copies the complete bitmap, but positions it at the point specified by the parameter (x!, y!).

The third form copies a portion of the bitmap, specified by x1,y1 as the upper left corner and x2,y2 as the lower right corner. It is positioned at the point specified by the parameter (x,y). You must use care that your parameters are valid for the specified bitmaps, or results of the operation are undefined.

If the style parameter is included, it is one of the values in the following table. If not included, a default of %mix_CopySrc is presumed. There are 8 mix modes available to use for mixing drawing colors with the colors which already exist at the at the drawing location The mix mode are predefined in PowerBASIC.

%mix_NotMergeSrc | Pixel is the inverse of the MergeSrc color.
%mix_NotCopySrc | Pixel is the inverse of the source color.
%mix_MaskSrcNot | Pixel is a combination of the colors common to both the source and the inverse of the pixel.
%mix_XorSrc | Pixel is a combination of the colors in the source and the pixel, but not in both.
%mix_MaskSrc | Pixel is a combination of the colors common to both the source and the pixel.
%mix_MergeNotSrc | Pixel is a combination of the pixel color and the and the inverse of the source color.
%mix_CopySrc | Pixel is the source color (default).
%mix_MergeSrc | Pixel is a combination of the source color and the pixel color.

See also

GRAPHIC ATTACH, GRAPHIC RENDER, GRAPHIC STRETCH


GRAPHIC ELLIPSE statement

Purpose

Draw an ellipse or a circle in the selected graphic window.

Syntax

GRAPHIC ELLIPSE (x1!, y1!) - (x2!, y2!) [, [rgbColor&] [,

[fillcolor&] [, [fillstyle&]]]]

Remarks

The graphic target must first be selected with GRAPHIC ATTACH. The Coordinates are specified in the same terms (pixels or dialog units) as the parent dialog (or world coordinates, if those were chosen with GRAPHIC SCALE). Line width can be set using GRAPHIC WIDTH. If line width is set to 1 (the default), the line style can be set with GRAPHIC STYLE. Because of the nature of an ellipse, which has no obvious beginning or end, GRAPHIC ELLIPSE neither uses, nor updates, the last point referenced (POS).

x1!, y1!

The upper left corner of the bounding rectangle.

x2!, y2!

The lower right corner of the bounding rectangle.

rgbColor&

Optional RGB color of the ellipse edge. If omitted (or -1), the edge color defaults to the current foreground color for the selected graphic window.

fillcolor&

Optional RGB color of the ellipse interior. If fillcolor& is omitted (or -2), the interior of the ellipse is not filled, allowing the background to show through. If fillcolor& is -1, the interior is painted with the same color as the edge. Otherwise, fillcolor& specifies the RGB color to be used.

fillstyle&

Optional fill style (pattern) to be used. If fillstyle& is omitted, the default fill style is solid (0). If a hatch pattern is chosen (1 to 6), the foreground color is specified by the fillcolor&, while the background is specified by the default background color for the selected graphic window. The optional fillstyle& may be:

0 | Solid (default)
1 | Horizontal Lines
2 | Vertical Lines
3 | Upward Diagonal Lines
4 | Downward Diagonal Lines
5 | Crossed Lines
6 | Diagonal Crossed Lines

See also

GRAPHIC ARC, GRAPHIC ATTACH, GRAPHIC COLOR, GRAPHIC LINE, GRAPHIC PIE, GRAPHIC STYLE, GRAPHIC WIDTH

Example

' Draw a circle, using default colors.

GRAPHIC ELLIPSE (10, 10) - (100, 100)

' Draw a blue ellipse filled with a light-gray,

' diagonal cross pattern.

GRAPHIC ELLIPSE (15, 25) - (95, 50), %BLUE, RGB(191,191,191), 6


GRAPHIC FONT statement

Purpose

Select a font for the GRAPHIC PRINT statement.

Syntax

GRAPHIC FONT fontname$, points&, style&

Remarks

The graphic window must first be selected with GRAPHIC ATTACH. If no GRAPHIC FONT statement is executed, the default font is MS Sans Serif, 8 point, with normal style. If the requested font is not available on the computer, Windows will normally substitute another font, which may have similar attributes.

fontname$

The name of the font.

points&

The size of the font, in points.

style&

Font style attribute. Any of the following values can be combined or used alone:

0 | normal
1 | bold
2 | italic
4 | underline
8 | strikeout

For example, a style& of 3 specifies a combination of both bold and italic attributes.

See also

GRAPHIC ATTACH, GRAPHIC CHR SIZE, GRAPHIC PRINT, GRAPHIC TEXT SIZE

Example

' Set the font for the selected graphic window to

' Times New Roman, 18 points, bold + italic + underline = (1+2+4).

GRAPHIC FONT "Times New Roman", 18, 7


GRAPHIC GET BITS statement

Purpose

Retrieve a copy of a bitmap , storing it as a device-independent bitmap in a dynamic string variable.

Syntax

GRAPHIC GET BITS TO bitvar$

Remarks

This statement retrieves a copy of the entire bitmap for the selected graphic target, assigning it to the dynamic string variable specified by bitvar$. This allows you to make many modifications to the bitmap very quickly, particularly operations which may not be directly supported by PowerBASIC. For example, you might change all red pixels in a bitmap to blue. Once your operations are complete, the bitmap is replaced using GRAPHIC SET BITS.

The bitvar$ string will contain a series of four-byte values, each of which represents a long integer. You can convert the four-byte string sections to numeric values with the CVL function, and convert a numeric value to a four-byte string with MKL$. The first four-byte value specifies the width of the bitmap, in pixels, and the second specifies the height. Following that will be one four-byte value for each pixel in the bitmap, which represents the color of that pixel. So, a 20 by 20 bitmap would have 400 pixels and require 1600 bytes (400 * 4), plus 4 bytes for the width and 4 bytes for the height, or a total of 1608 bytes.

The first four-byte pixel value in the string represents the top-left corner of the image, the second represents the second pixel of the first row, and so on. After the last pixel of the first row will be the first pixel of the second row, etc.

If execution speed is most important, it's likely that the string can be manipulated most efficiently with pointer variables.

Some Windows API functions, namely those which reference Device-Independent Bitmaps (DIB), require that colors be specified in the reverse of normal RGB sequence (Blue-Green-Red instead of Red-Green-Blue). To maximize performance, GRAPHIC GET BITS uses BGR format as well. You can use the BGR() function to translate an RGB value to its BGR equivalent.

See also

BGR, CVL, GRAPHIC SET BITS, MKL$, RGB

Example

' Change all red pixels to blue

LOCAL PixelPtr AS LONG PTR

GRAPHIC GET BITS TO bmp$

xsize& = CVL(bmp$,1)

ysize& = CVL(bmp$,5)

PixelPtr = STRPTR(bmp$) + 8

FOR i& = 1 TO xsize& * ysize&

IF @PixelPtr = BGR(%red) THEN @PixelPtr = BGR(%blue)

INCR PixelPtr

NEXT

GRAPHIC SET BITS bmp$


GRAPHIC GET CLIENT statement

Purpose | Retrieve the client size of the selected graphic target.
Syntax | GRAPHIC GET CLIENT TO ncWidth!, ncHeight!
Remarks | GRAPHIC GET CLIENT allows you to retrieve the size of the client area (displayable area) of the selected graphic window or bitmap. The size is specified in the same terms (pixels or dialog units) as the parent dialog (or world coordinates, if those were chosen with GRAPHIC SCALE). If no graphic target has been selected with GRAPHIC ATTACH, the values 0,0 are returned.
See also | GRAPHIC ATTACH


GRAPHIC GET DC statement

Purpose | Retrieve the handle of the DC (device context) for the selected graphic window.
Syntax | GRAPHIC GET DC TO hDC???
Remarks | The DC handle may be used with various Windows API functions to perform specialized graphic operations in the graphic window. The graphic window must first be selected with GRAPHIC ATTACH. If no graphic window is currently selected, zero is returned.
See also | GRAPHIC ATTACH


GRAPHIC GET LOC statement

Purpose | Retrieve the location of the selected graphic window on the screen.
Syntax | GRAPHIC GET LOC TO x&, y&
Remarks | This statement allows you to retrieve the location of the selected graphic window. If no graphic object is selected, or it is not a graphic window, 0,0 is returned. The location is always specified in the same terms (pixels or dialog units) as the parent dialog, relative to the upper left corner of the screen.
See also | GRAPHIC ATTACH, GRAPHIC GET PPI, GRAPHIC SET LOC


GRAPHIC GET MIX statement

Purpose

Retrieve the color mix mode for the selected graphic target.

Syntax

GRAPHIC GET MIX TO mixmode&

Remarks

The graphic window must first be selected with GRAPHIC ATTACH. There are 16 mix modes available to use for mixing the drawing color with the color that already exists at the drawing location.

%mix_Blackness | Pixel is always 0 (black).
%mix_NotMergeSrc | Pixel is the inverse of the MergeSrc color.
%mix_MaskNotSrc | Pixel is a combination of the colors common to both the pixel and the inverse of the source.
%mix_NotCopySrc | Pixel is the inverse of the pen color.
%mix_MaskSrcNot | Pixel is a combination of the colors common to both the source and the inverse of the pixel.
%mix_Not | Pixel is the inverse of the pixel color.
%mix_XorSrc | Pixel is a combination of the colors in the source and in the pixel, but not in both.
%mix_NotMaskSrc | Pixel is the inverse of the MaskSrc color.
%mix_MaskSrc | Pixel is a combination of the colors common to both the source and the pixel.
%mix_NotXorSrc | Pixel is the inverse of the XorSrc color.
%mix_Nop | Pixel remains unchanged.
%mix_MergeNotSrc | Pixel is a combination of the source color and the inverse of the pixel color.
%mix_CopySrc | Pixel is the source color (default).
%mix_MergeSrcNot | Pixel is a combination of the source color and the inverse of the pixel color.
%mix_MergeSrc | Pixel is a combination of the source color and the pixel color.
%mix_Whiteness | Pixel is always 1 (white).

See also

GRAPHIC ATTACH, GRAPHIC SET MIX


GRAPHIC GET PIXEL statement

Purpose | Retrieve the color of the pixel at the specified point in the selected graphic window.
Syntax | GRAPHIC GET PIXEL (x!, y!) TO rgbColor&
Remarks | The graphic window must first be selected with GRAPHIC ATTACH. The coordinate points x, y are specified in the same terms (pixels or dialog units) as the parent dialog (or world coordinates, if those were chosen with GRAPHIC SCALE).
See also | GRAPHIC ATTACH, GRAPHIC COLOR, GRAPHIC SET PIXEL


GRAPHIC GET POS statement

Purpose

Retrieve the POS (last point referenced) by a statement.

Syntax

GRAPHIC GET POS TO x!, y!

Remarks

The graphic window must first be selected with GRAPHIC ATTACH. The coordinate points x, y are specified in the same terms (pixels or dialog units) as the parent dialog (or world coordinates, if those were chosen with GRAPHIC SCALE).

See also

GRAPHIC ATTACH, GRAPHIC SET POS


GRAPHIC GET PPI statement

Purpose

Retrieve the resolution of the display device, in points per inch.

Syntax

GRAPHIC GET PPI TO ncWidth&, ncHeight&

Remarks

The graphic window must first be selected with GRAPHIC ATTACH. The resolution is always specified in pixels, regardless of any GRAPHIC SCALE option. This statement is particularly useful in drawing items such as rulers and graphs to a "representative physical size". There are 25.4 millimeters per inch, so just divide by 25.4 to convert from pixels per inch to pixels per millimeter.

"Representative physical size" means that the actual image may be close to a particular physical size, but is subject to factors including Windows default PPI setting, the driver's DPI to PPI ratio and even how the monitor has been adjusted. By using the GRAPHIC GET PPI, results, you can construct a representative graphic image that can be saved and later output at the intended scale by more precise means, for example a higher resolution Windows printer.

See also

GRAPHIC ATTACH


GRAPHIC LINE statement

Purpose

Draw a line

Syntax

GRAPHIC LINE [STEP] [(x1!, y1!)] - [STEP] (x2!, y2!)[, rgbColor&]

Remarks

The line is drawn from the first point, up to, but not including the second point. Coordinate points are specified in the same terms (pixels or dialog units) as the parent dialog (or world coordinates, if those were chosen with GRAPHIC SCALE). Line width can be set using GRAPHIC WIDTH. If line width is set to 1 (the default), the line style can be set with GRAPHIC STYLE.

x1!, y1!

Optional values which define the starting point of the line. If this optional first point is omitted, the line begins at the last point referenced (POS) in a preceding statement. If the first STEP option is included, the x1! and y1! starting coordinates are relative to the last point referenced (POS).

x2!, y2!

The ending point of the line. If the second STEP option is included, the x2! and y2! ending coordinates are relative to the starting coordinates.

rgbColor&

Optional RGB color value for the line. If rgbColor& is omitted (or -1), the line color defaults to the current foreground color.

See also

GRAPHIC ARC, GRAPHIC ATTACH, GRAPHIC BOX, GRAPHIC COLOR, GRAPHIC ELLIPSE, GRAPHIC PIE, GRAPHIC STYLE, GRAPHIC WIDTH

Example

' Draw a triangle. Note that, since LINE draws up to,

' but not including the second point, one extra point

' must be added when STEP is used.

GRAPHIC LINE (10, 10) - (10, 100) ' left side

GRAPHIC LINE STEP - (101, 100) ' base line

GRAPHIC LINE STEP - (10, 10) ' back to top


GRAPHIC PAINT statement

Purpose

Fill an area with a solid color or a hatch pattern.

Syntax

GRAPHIC PAINT [BORDER | REPLACE] [STEP] (x!, y!) [, [rgbFill&] [,

[rgbBorder&] [, [fillstyle&]]]]

Remarks

The graphic target must first be chosen with GRAPHIC ATTACH. The coordinate points are specified in the same terms (pixels or dialog units) as the parent dialog (or world coordinates, if those were chosen with GRAPHIC SCALE).

x!, y!

The point where filling begins. If the STEP option is included, the x and y coordinates are relative to the last point referenced (POS) in the selected graphic target.

rgbFill&

Optional RGB color value for the fill area. If rgbFill& is omitted (or -1), the default foreground color is used.

rgbBorder&

Optional RGB base color of the fill area. If the REPLACE option is chosen, filling continues outward in all directions until a color other than rgbBorder& is found. If the BORDER option (or no option) is included, filling continues outward until the rgbBorder& color is found. If rgbBorder& is not specified, it is assumed to be the same as the rgbFill& parameter.

fillstyle&

Optional fill style (pattern) to use. If fillstyle& is omitted, the default fill style is solid (0). If a hatch pattern is chosen (1 to 6), the foreground color is specified by the rgbFill&, while the background is specified by the default background color for the selected graphic target. The optional fillstyle& may be:

0 | Solid (default)
1 | Horizontal Lines
2 | Vertical Lines
3 | Upward Diagonal Lines
4 | Downward Diagonal Lines
5 | Crossed Lines
6 | Diagonal Crossed Lines

See also

GRAPHIC ARC, GRAPHIC ATTACH, GRAPHIC BOX, GRAPHIC COLOR, GRAPHIC ELLIPSE, GRAPHIC LINE, GRAPHIC PIE

Example

FUNCTION PBMAIN

LOCAL hWin AS DWORD

GRAPHIC WINDOW "Paint", 0, 0, 200, 200 TO hWin

GRAPHIC ATTACH hWin, 0

' Draw a circle with blue foreground color

' and a box below it with red foreground color.

GRAPHIC ELLIPSE (10, 10) - (70, 70), %BLUE

GRAPHIC BOX (10, 80) - (70, 120), 0, %RED

' Fill the area inside the circle's blue borders

' with a green diagonal pattern.

GRAPHIC PAINT BORDER (40, 40), %GREEN, %BLUE, 6

'Retrieve the color at point 5,5 (outside the circle).

GRAPHIC GET PIXEL (5, 5) TO lRes&

' Fill the area outside the circle by replacing the color

' at point 5,5 and outwards with a solid yellow color.

GRAPHIC PAINT REPLACE (5, 5), RGB(255, 255, 223), lRes&, 0

SLEEP 10000

END FUNCTION


GRAPHIC PIE statement

Purpose

Draw a pie section on the selected graphic target.

Syntax

GRAPHIC PIE (x1!, y1!) - (x2!, y2!), arcStart!, arcEnd! [,

[rgbColor&] [, [fillcolor&] [, [fillstyle&]]]]

Remarks

A pie section is an arc, with a line drawn from each end point to the center of the circle or ellipse. To specify a pie section, you would first define the full circle or ellipse of which it is a part, and then specify the points on the ellipse where the arc starts and stops.

The full circle or ellipse is defined by its bounding rectangle, which is the smallest rectangle which can be drawn around the circle or ellipse. For example, if the circle is centered at position (400,400), with a radius of 100 pixels, the upper left corner (x1,y1) of the bounding rectangle is (300,300), and the lower right corner (x2,y2) is (500,500).

The start point and end point of the arc are specified by their angle, which must be given in radians. A complete circle or ellipse is 2*pi radians. On a 12-hour clock-face, the values 0 and 2*pi both refer to the position of 3 o'clock, while the value 1*pi refers to the position of 9 o'clock. Other positions are specified by a radian value relative to these. In PowerBASIC, arcs are always drawn counter-clockwise from the starting point to the ending point.

Prior to any graphical operations, the graphic target must first be selected with GRAPHIC ATTACH. The Coordinates are specified in the same terms (pixels or dialog units) as the parent dialog (or world coordinates, if those were chosen with GRAPHIC SCALE). Line width can be set using GRAPHIC WIDTH. If line width is set to 1 (the default), the line style can be set with GRAPHIC STYLE. Because of the nature of a pie section, GRAPHIC PIE neither uses, nor updates, graphic POS (last point referenced).

x1!, y1!

The upper left corner of the bounding rectangle of the full circle or ellipse.

x2!, y2!

The lower right corner of the bounding rectangle of the full circle or ellipse.

ArcStart!

The starting angle of the arc, in radians, from 0 to 2*pi.

ArcEnd!

The ending angle of the arc, in radians, from 0 to 2*pi radians. Note that arcs are always drawn counter-clockwise from arcStart! to arcEnd!. Compared with a 12-hour clock-face, 0 or 2*pi radians is at 3 o'clock, and 1*pi radians is at 9 o'clock.

rgbColor&

Optional RGB color of the pie section edge. If omitted (or -1), the edge color defaults to the current foreground color.

fillcolor&

Optional RGB color of the pie section interior. If fillcolor& is omitted (or -2), the interior of the pie section is not filled, allowing the background to show through. If fillcolor& is -1, the interior is painted with the same color as the edge. Otherwise, fillcolor& specifies the RGB color to be used.

fillstyle&

Optional fill style (pattern) to be used. If fillstyle& is omitted, the default fill style is solid (0). If a hatch pattern is chosen (1 to 6), the foreground color is specified by the fillcolor&, while the background is specified by the default background color. The optional fillstyle& may be:

0 | Solid (default)
1 | Horizontal Lines
2 | Vertical Lines
3 | Upward Diagonal Lines
4 | Downward Diagonal Lines
5 | Crossed Lines
6 | Diagonal Crossed Lines

See also

GRAPHIC ARC, GRAPHIC ATTACH, GRAPHIC BOX, GRAPHIC COLOR, GRAPHIC ELLIPSE, GRAPHIC LINE, GRAPHIC STYLE, GRAPHIC WIDTH

Example

FUNCTION PBMAIN

LOCAL hWin AS DWORD

GRAPHIC WINDOW "Pie", 0, 0, 200, 200 TO hWin

GRAPHIC ATTACH hWin, 0

' A full circle is 2Pi radians (100%).

' To show a 25% Pie, use the formula 0.25 * 2Pi.

' The following divides a full circle into four 25% parts, each

' with its own colors, each slightly separated from the others.

' Note: 0 is at 3 O'clock, then it builds counter-clockwise.

LOCAL Pi2 AS DOUBLE

Pi2 = 8 * ATN(1) ' 2 * Pi can be useful here

GRAPHIC PIE (10, 9)-(110, 109), 0, Pi2 * 0.25, %BLUE, %LTGRAY, 3

GRAPHIC PIE (9, 9)-(109, 109), Pi2 * 0.25, Pi2 * 0.50, %RED, %LTGRAY, 4

GRAPHIC PIE (9, 10)-(109, 110), Pi2 * 0.5, Pi2 * 0.75, RGB(0,127,0), %LTGRAY, 3

GRAPHIC PIE (10, 10)-(110, 110), Pi2 * 0.75, 0, %GRAY, %LTGRAY, 4

SLEEP 10000

END FUNCTION


GRAPHIC POLYGON statement

Purpose

Draw a polygon in the selected graphic target.

Syntax

GRAPHIC POLYGON points [,[rgbColor&] [, [fillcolor&] [,[fillstyle&] [, fillmode&]]]]

Remarks

The graphic target must first be selected with GRAPHIC ATTACH. The Coordinates are specified in the same terms (pixels or dialog units) as the parent dialog (or world coordinates, if those were chosen with GRAPHIC SCALE). Line width can be set using GRAPHIC WIDTH. If line width is set to 1 (the default), the line style can be set with GRAPHIC STYLE. GRAPHIC POLYGON neither uses, nor updates, the last point referenced (POS).

points

User-defined type that defines the number of vertices and the location of each. There must be at least two, and no more than 1024 vertices. The first member is a long integer point count, followed directly by the appropriate number of single precision floats to specify the actual coordinates. Floating point coordinates are required, because of the possibility of their use as world coordinates with SCALE. You can use a type with a scalar list, like this:

TYPE PolyPoints

count as long

x1 as single

y1 as single

x2 as single

y2 as single

x3 as single

y3 as single

END TYPE

Or, you can create an array using point types, like this:

TYPE PolyPoint

x as single

y as single

END TYPE

TYPE PolyArray

count as long

xy(1 TO 3) as PolyPoint

END TYPE

rgbColor&

Optional RGB color of the polygon edge. If omitted (or -1), the edge color defaults to the current foreground color.

fillcolor&

Optional RGB color of the polygon interior. If fillcolor& is omitted (or -2), the interior of the ellipse is not filled, allowing the background to show through. If fillcolor& is -1, the interior is painted with the same color as the edge. Otherwise, fillcolor& specifies the RGB color to be used.

fillstyle&

Optional fill style (pattern) to be used. If fillstyle& is omitted, the default fill style is solid (0). If a hatch pattern is chosen (1 to 6), the foreground color is specified by the fillcolor&, while the background is specified by the default background color. The optional fillstyle& may be:

0 | Solid (default)
1 | Horizontal Lines
2 | Vertical Lines
3 | Upward Diagonal Lines
4 | Downward Diagonal Lines
5 | Crossed Lines
6 | Diagonal Crossed Lines

fillmode&

If fillmode& is missing (or zero), the winding mode is selected. This fills any region with a non-zero winding value. If fillmode& is non-zero, the alternate mode is selected. This fills the area between odd-numbered and even-numbered polygon sides on each scan line. That is, it fills the area between the first side and the second side, between the third side and fourth side, etc.

See also

GRAPHIC ARC, GRAPHIC ATTACH, GRAPHIC BOX, GRAPHIC COLOR, GRAPHIC ELLIPSE, GRAPHIC LINE, GRAPHIC POLYLINE, GRAPHIC STYLE, GRAPHIC WIDTH


GRAPHIC POLYLINE statement

Purpose | Draw a series of connected line segments.
Syntax | GRAPHIC POLYLINE points [, rgbColor&]
Remarks | The graphic target must first be selected with GRAPHIC ATTACH. The Coordinates are specified in the same terms (pixels or dialog units) as the parent dialog (or world coordinates, if those were chosen with GRAPHIC SCALE). Line width can be set using GRAPHIC WIDTH. If line width is set to 1 (the default), the line style can be set with GRAPHIC STYLE. GRAPHIC POLYLINE neither uses, nor updates, the last point referenced (POS).
points | User-defined type that defines the number of vertices and the location of each. There must be at least two, and no more than 1024 vertices. The first member is a long integer point count, followed directly by the appropriate number of single precision floats to specify the actual coordinates. Floating point coordinates are required, because of the possibility of their use as world coordinates with SCALE. You can use a type with a scalar list, like this: TYPE PolyPoints count as long x1 as single y1 as single x2 as single y2 as single x3 as single y3 as single END TYPE Or, you can create an array using point types, like this: TYPE PolyPoint x as single y as single END TYPE TYPE PolyArray count as long xy(1 TO 3) as PolyPoint END TYPE
rgbColor& | Optional RGB color of the polyline. If omitted (or -1), the color defaults to the current foreground color.
See also | GRAPHIC ARC, GRAPHIC ATTACH, GRAPHIC BOX, GRAPHIC COLOR, GRAPHIC ELLIPSE, GRAPHIC LINE, GRAPHIC POLYGON, GRAPHIC STYLE, GRAPHIC WIDTH


GRAPHIC PRINT statement

Purpose

Output text to the selected graphic target.

Syntax

GRAPHIC PRINT expr [; expr] [;]

Remarks

Prior to any graphical operations, the graphic target must first be selected with GRAPHIC ATTACH. The text color and the text background color are set with GRAPHIC COLOR. Text which extends beyond the bounds of the graphic target is clipped. The size of the text to be printed can be determined in advance with the GRAPHIC TEXT SIZE statement.

expr

A numeric or string expression to be drawn on the selected graphic target. A semicolon can be used as separator between multiple expressions in the same statement. Drawing begins at the last point referenced (POS) by another statement, or the point specified by GRAPHIC SET POS. The upper left corner of the text is positioned at the POS. Upon completion, the POS is moved to the left margin of the next line. However, if a trailing semi-colon is included, movement to the next line is suppressed.

GRAPHIC PRINT USING$ is valid but USING$ will be evaluated as a string expression, see the USING$ function for more information.

See also

GRAPHIC ATTACH, GRAPHIC CHR SIZE, GRAPHIC FONT, GRAPHIC GET POS, GRAPHIC SET POS, GRAPHIC TEXT SIZE, USING$


GRAPHIC REDRAW statement

Purpose

Update buffered graphical statements, drawing them to the selected graphic target.

Syntax

GRAPHIC REDRAW

Remarks

This statement is only needed when GRAPHIC ATTACH with the REDRAW option have been chosen for faster, buffered draw operations. Otherwise, it performs no operation.

All PowerBASIC graphical displays are persistent -- they are automatically redrawn for you after resuming from being minimized or temporarily covered by other windows.

See also

GRAPHIC ATTACH

Example

FUNCTION PBMAIN

LOCAL hWin AS DWORD

' Draw a buffered, blue gradient fill

GRAPHIC WINDOW "Gradient", 0, 0, 255, 255 TO hWin

GRAPHIC ATTACH hWin, 0, REDRAW

FOR y& = 0 TO 255

GRAPHIC LINE (0, y&) - (255, y&), RGB(0, 0, y&)

NEXT

GRAPHIC REDRAW

SLEEP 10000

END FUNCTION


GRAPHIC RENDER statement

Purpose

Render an image on the selected graphic target.

Syntax

GRAPHIC RENDER BmpName$, (x1!, y1!)-(x2!, y2!)

Remarks

Renders an image, loaded from a resource or a disk file, on the selected graphic or window. The parameter BmpName$ contains the name of an image to be loaded. If BmpName$ contains a period, it is presumed to be the name of a disk file. Otherwise, an attempt is made to load it from the program’s resource data; if not found, it is then presumed to be a disk file. The parameters x1!,. y1! define the upper left corner of the destination rectangle, while x2!, y2! define the lower right corner of that rectangle. If the destination rectangle is larger or smaller than the original, the image is stretched or shrunk to the requested size.

The following code will retrieve the natural size of an image in a bitmap file, in pixels:

nFile& = FREEFILE

OPEN "myimage.bmp" FOR BINARY AS nFile&

GET #nFile&, 19, nWidth&

GET #nFile&, 23, nHeight&

CLOSE nFile&

See also

GRAPHIC ATTACH, GRAPHIC COPY, GRAPHIC STRETCH


GRAPHIC SAVE statement

Purpose

Save an image to a bitmap (.BMP) file.

Syntax

GRAPHIC SAVE BmpName$

Remarks

The selected graphic target (a graphic , window, etc.) is saved to a disk file using the filename specified by BmpName$. The bitmap is always saved in a single plane, 24-bit format, to allow for the maximum (true color) resolution.

See also

GRAPHIC ATTACH, GRAPHIC BITMAP LOAD


GRAPHIC SCALE statement

Purpose

Define a custom coordinate system for the graphic target.

Syntax

GRAPHIC SCALE (x1!, y1!) - (x2!, y2!)

Remarks

The graphic target must first be chosen with GRAPHIC ATTACH. GRAPHIC SCALE lets you define your own world coordinate system for subsequent statements. The custom coordinates remain with the graphic target until GRAPHIC SCALE is repeated, or the target is deleted. World coordinates may be floating point values, with the only requirement that x1! not equal x2!, and y1! not equal y2!. If either is equal, the statement is ignored.

If x2! is greater than x1!, coordinates grow larger as they move to the right. Otherwise, they grow larger as they move to the left.

If y2! is greater than y1!, coordinates grow larger as they move downward. Otherwise, they grow larger as they move upward.

See also

GRAPHIC ATTACH


GRAPHIC SET BITS statement

Purpose

Replace a copy of a bitmap that was retrieved as a device-independent bitmap.

Syntax

GRAPHIC SET BITS bitexpr$

Remarks

This statement replaces a bitmap that was originally retrieved with the GRAPHIC GET BITS statement. The bitmap is assigned to the selected graphic target from the string expression specified by bitexpr$. This allows you to make many modifications to the bitmap very quickly, particularly operations which may not be directly supported by PowerBASIC. For example, a typical use might be to change all red pixels in a bitmap to blue.

The bitexpr$ string contains a series of four-byte values, each of which represents a long integer. You can convert the four-byte string sections to numeric values with the CVL function, and convert a numeric value to a four-byte string with MKL$. The first four-byte value specifies the width of the bitmap, in pixels, and the second specifies the height. Following that will be one four-byte value for each pixel in the bitmap, which represents the color of that pixel. So, a 20 by 20 bitmap would have 400 pixels and require 1600 bytes (400 * 4), plus 4 bytes for the width and 4 bytes for the height, or a total of 1608 bytes.

The first four-byte pixel value in the string represents the top-left corner of the image, the second represents the second pixel of the first row, and so on. After the last pixel of the first row will be the first pixel of the second row, etc.

If execution speed is most important, it's likely that the string can be manipulated most efficiently with pointer variables.

Some Windows API functions, namely those which reference Device-Independent Bitmaps (DIB), require that colors be specified in the reverse of normal RGB sequence (Blue-Green-Red instead of Red-Green-Blue). To maximize performance, GRAPHIC SET BITS uses BGR format as well. You can use the BGR function to translate an RGB value to its BGR equivalent.

See also

BGR, CVL, GRAPHIC GET BITS, MKL$, RGB

Example

' Change all red pixels to blue

LOCAL PixelPtr AS LONG PTR

GRAPHIC GET BITS TO bmp$

xsize& = CVL(bmp$,1)

ysize& = CVL(bmp$,5)

PixelPtr = STRPTR(bmp$) + 8

FOR i& = 1 TO xsize& * ysize&

IF @PixelPtr = BGR(%red) THEN @PixelPtr = BGR(%blue)

INCR PixelPtr

NEXT

GRAPHIC SET BITS bmp$


GRAPHIC SET FOCUS statement

Purpose | Bring the selected graphic window to the foreground and direct focus to it.
Syntax | GRAPHIC SET FOCUS
Remarks | A graphic window must first be chosen with GRAPHIC ATTACH. The GRAPHIC SET FOCUS statement brings the graphic window to the foreground, directing focus to it. This is particularly useful when another window may overlap the graphic window.
See also | GRAPHIC ATTACH


GRAPHIC SET LOC statement

Purpose | Change the location of the selected graphic window on the screen.
Syntax | GRAPHIC SET LOC x&, y&
Remarks | This statement allows you to change the location of the selected graphic window. If no graphic target is selected, or it is not a graphic window, no action is taken. The location is always given in pixels, relative to the upper left corner of the screen.
See also | GRAPHIC ATTACH, GRAPHIC GET LOC, GRAPHIC GET PPI


GRAPHIC SET MIX statement

Purpose

Set the color mix mode for the selected graphic target.

Syntax

GRAPHIC SET MIX mode&

Remarks

Prior to any graphical operations, the graphic target must first be selected with GRAPHIC ATTACH. There are 16 mix modes available to use for mixing the drawing color with the color that already exists at the drawing location. The mix mode equates are predefined in PowerBASIC.

%mix_Blackness | Pixel is always 0 (black).
%mix_NotMergeSrc | Pixel is the inverse of the MergeSrc color.
%mix_MaskNotSrc | Pixel is a combination of the colors common to both the pixel and the inverse of the source.
%mix_NotCopySrc | Pixel is the inverse of the pen color.
%mix_MaskSrcNot | Pixel is a combination of the colors common to both the source and the inverse of the pixel.
%mix_Not | Pixel is the inverse of the pixel color.
%mix_XorSrc | Pixel is a combination of the colors in the source and in the pixel, but not in both.
%mix_NotMaskSrc | Pixel is the inverse of the MaskSrc color.
%mix_MaskSrc | Pixel is a combination of the colors common to both the source and the pixel.
%mix_NotXorSrc | Pixel is the inverse of the XorSrc color.
%mix_Nop | Pixel remains unchanged.
%mix_MergeNotSrc | Pixel is a combination of the source color and the inverse of the pixel color.
%mix_CopySrc | Pixel is the source color (default).
%mix_MergeSrcNot | Pixel is a combination of the source color and the inverse of the pixel color.
%mix_MergeSrc | Pixel is a combination of the source color and the pixel color.
%mix_Whiteness | Pixel is always 1 (white).

See also

GRAPHIC ATTACH, GRAPHIC GET MIX


GRAPHIC SET PIXEL statement

Purpose | Draw a single pixel.
Syntax | GRAPHIC SET PIXEL [STEP] (x!, y!) [, rgbColor&]
Remarks | The graphic window must first be chosen with GRAPHIC ATTACH. The coordinate point is specified in pixels, unless optional world coordinates have been requested with a GRAPHIC SCALE statement. If the STEP option is included, the x! and y! coordinates are relative to the last point referenced (POS).
See also | GRAPHIC ATTACH, GRAPHIC COLOR, GRAPHIC GET PIXEL


GRAPHIC SET POS statement

Purpose | Set the last point referenced (POS) for the selected graphic target.
Syntax | GRAPHIC SET POS [STEP] (x!, y!)
Remarks | The graphic window must first be chosen with GRAPHIC ATTACH. The coordinate point is specified in the same terms (pixels or dialog units) as the parent dialog (or world coordinates, if those were chosen with GRAPHIC SCALE). If the STEP option is included, the x! and y! coordinates are relative to the last point referenced (POS).
See also | GRAPHIC ATTACH, GRAPHIC GET POS


GRAPHIC STRETCH statement

Purpose

Copy and resize a to the selected graphic target.

Syntax

GRAPHIC STRETCH hbmpSource???, id&, (x1!, y1!)-(x2!, y2!) TO (x3!,

y3!)-(x4!,y4!) [, style%]

Remarks

You can copy a complete bitmap, or a portion of it, to the selected graphic target, while resizing it to a larger or smaller size. The expression hbmpSource??? specifies the handle of the source bitmap or window. The expression id& is reserved for future use, and must always be set to zero. The destination of the copy operation is the target specified by GRAPHIC ATTACH. The bitmap is automatically resized to fit the destination parameters. You must use care that your parameters are valid for the specified bitmaps, or results of the operation are undefined.

If style& is included, it is one of the following values:

%mix_NotMergeSrc | Pixel is the inverse of the MergeSrc color.
%mix_NotCopySrc | Pixel is the inverse of the source color.
%mix_MaskSrcNot | Pixel is a combination of the colors common to both the source and the inverse of the pixel.
%mix_XorSrc | Pixel is a combination of the colors in the source and the pixel, but not in both.
%mix_MaskSrc | Pixel is a combination of the colors common to both the source and the pixel.
%mix_MergeNotSrc | Pixel is a combination of the pixel color and the and the inverse of the source color.
%mix_CopySrc | Pixel is the source color (default).
%mix_MergeSrc | Pixel is a combination of the source color and the pixel color.

See also

GRAPHIC ATTACH, GRAPHIC COPY, GRAPHIC RENDER


GRAPHIC STYLE statement

Purpose

Set the line style to be used by various statements in the selected graphic target.

Syntax

GRAPHIC STYLE linestyle&

Remarks

The graphic window must first be selected with GRAPHIC ATTACH. Due to limitations in the Windows graphics device interface (GDI), styles are only applied if the line width is set to 1, the default. If the line width is greater than 1, the style is interpreted as 0, solid.

Available line styles are:

0 | Solid (default)
1 | Dash
2 | Dot
3 | DashDot
4 | DashDotDot

See also

GRAPHIC ARC, GRAPHIC ATTACH, GRAPHIC BOX, GRAPHIC ELLIPSE, GRAPHIC LINE, GRAPHIC PIE, GRAPHIC WIDTH

Example

' Draw a square box with red, dotted lines

GRAPHIC WIDTH 1

GRAPHIC STYLE 2

GRAPHIC BOX (10, 10) - (110, 110), 0, %RED


GRAPHIC TEXT SIZE statement

Purpose

Calculate the size of text to be printed.

Syntax

GRAPHIC TEXT SIZE txt$ TO nWidth!, nHeight!

Remarks

This statement calculates the total size of the printed text, based upon the current font for the graphic target. The sizes returned are specified in pixels, unless optional world coordinates have been defined with a GRAPHIC SCALE statement.

This allows you to easily calculate the appropriate print position, particularly when using a proportional font.

See also

GRAPHIC ATTACH, GRAPHIC CHR SIZE, GRAPHIC FONT, GRAPHIC PRINT


GRAPHIC WIDTH statement

Purpose

Set the line width to be used by various statements in the selected graphic target.

Syntax

GRAPHIC WIDTH linewidth&

Remarks

The graphic target must first be selected with GRAPHIC ATTACH. If line width is set to a value greater than 1 (default), the line style is always interpreted to be 0 (solid).

See also

GRAPHIC ARC, GRAPHIC ATTACH, GRAPHIC BOX, GRAPHIC ELLIPSE, GRAPHIC LINE, GRAPHIC PIE, GRAPHIC STYLE

Example

' Draw a square box with red, thick lines

GRAPHIC WIDTH 10

GRAPHIC BOX (10, 10) - (110, 110), 0, %RED


GRAPHIC WINDOW END statement

Purpose | Close and destroy the selected graphic window.
Syntax | GRAPHIC WINDOW END
Remarks | The graphic window must first be selected with GRAPHIC ATTACH. GRAPHIC WINDOW END can be used to close and destroy the selected graphic window at any time. Otherwise, the window is automatically destroyed when the program ends.
See also | GRAPHIC WINDOW


GRAPHIC WINDOW statement

Purpose

Create a new graphic window.

Syntax

GRAPHIC WINDOW caption$, x&, y&, nWidth&, nHeight& TO hWin???

Remarks

After a graphic window has been created, GRAPHIC ATTACH is used to choose it as the selected graphic target. You can then draw text, lines, circles, and other forms with various statements. Due to the nature of a GRAPHIC WINDOW and its contents, it may not be resized. If a new size is needed, it should be destroyed and recreated.

GRAPHIC WINDOW END can be used to close and destroy the selected Graphic window at any time. Otherwise, the window is automatically destroyed when the program ends.

All PowerBASIC graphical displays are persistent -- they are automatically redrawn for you after resuming from being minimized or temporarily covered by other windows.

caption$

The text to be displayed in the title or caption bar of the graphic window. If caption$ is empty (zero-length), the window is displayed without a title bar, so the appearance is different and the window cannot be dragged by the user.

x&, y&

The location of the window, in pixels, relative to the upper left corner of the screen.

nWidth&

The width of the client area of the window, not including the frame. The width is specified in pixels.

nHeight&

The height of the client area of the window, not including the frame. The height is specified in pixels.

hWin???

The handle of the newly-created window. If the window could not be created, hWin??? will be 0.

See also

GRAPHIC ATTACH, GRAPHIC WINDOW END

Example

FUNCTION PBMAIN () AS LONG

' Create and show a Graphic window on screen

LOCAL hWin AS DWORD

GRAPHIC WINDOW "Box", 300, 300, 130, 130 TO hWin

GRAPHIC ATTACH hWin, 0

GRAPHIC BOX (10, 10) - (120, 120), 0, %BLUE

SLEEP 5000 ' show it for 5 seconds, then end

END FUNCTION


GUID$ function

Purpose

Return a 16-byte (128-bit) Globally Unique Identifier (GUID) or Universally Unique Identifier (UUID) binary .

Syntax

id$ = GUID$[()]

id$ = GUID$(guidtext$)

Remarks

The GUID$ function, with no parameter (or a null, zero-length string parameter) will return a new, unique 16-byte string GUID (Globally Unique Identifier). This GUID may be used as a new class identifier or an interface identifier, or for some other purpose where a unique identifier may be required, such as for a one-time encryption key.

If guidtext$ is specified, GUID$ examines a text string, and converts the first standard format, human-readable GUID it finds, and returns a 16-byte binary string. This 16-byte string contains the internal GUID representation as a 128-bit data item.

To be valid, the GUID string in guidtext$ string must contain exactly 32 hexadecimal digits, optionally delimited by spaces or hyphens, but which must be enclosed overall by curly braces. For example: "{01234567-89AB-CDEF-FEDC-BA9876543210}".

The GUID$ function is the logical complement to the GUIDTXT$ function.

id$

The return string may be assigned to a dynamic string, or a fixed-length string of at least 16 bytes, or (typically) a GUID variable. See DIM for more information on creating GUID variables.

Restrictions

If any errors are encountered, GUID$ returns a null (zero-length) string instead of the 16-byte GUID string. GUID$ can also be used in string equate assignments provided an explicit human-readable GUIDTXT$ argument string is assigned. For example:

$AppGuid = GUID$("{01234567-89AB-CDEF-FEDC-BA9876543210}")

See also

DIM, CLSID$, GUIDTXT$, INTERFACE/END INTERFACE, ISNOTHING, ISOBJECT, OBJECT, OBJACTIVE, OBJPTR, OBJRESULT, PROGID$, SET

Example

DIM oID1 AS GUID, oID2 AS GUID

oID1 = GUID$("{01234567-89AB-CDEF-FEDC-BA9876543210}")

oID2 = GUID$("The GUID we need is shown as {0123456789ABCDEFFEDCBA9876543210}")


GUIDTXT$ function

Purpose

Return a 38-byte human-readable Globally Unique Identifier (GUID) or Universally Unique Identifier (UUID) string from a 16-byte GUID .

Syntax

id$ = GUIDTXT$(guid16$)

Remarks

The GUIDTXT$ function takes a string parameter guid16$ that must be exactly 16-bytes long (and represents a 128-bit GUID string), and returns a 38-byte GUID text string. guid16$ is usually a GUID variable but may also be a dynamic or fixed-length string, etc.

The GUIDTXT$ function is the logical complement to the GUID$ function.

Restrictions

If any errors are encountered, GUIDTXT$ returns a null (zero-length) string instead of the 38-byte GUID text string.

See also

DIM, CLSID$, GUID$, INTERFACE/END INTERFACE, ISNOTHING, ISOBJECT, OBJECT, OBJACTIVE, OBJPTR, OBJRESULT, PROGID$, SET

Example

oID1$ = GUID$("{01234567-89AB-CDEF-FEDC-BA9876543210}")

oID2$ = GUIDTXT$(oID1$)


HEX$ function

Purpose

Return a that is the hexadecimal (base 16) representation of its argument.

Syntax

s$ = HEX$(numeric_expression [, digits])

Remarks

numeric_expression must be in the decimal range -2,147,483,648 to +4,294,967,295. Any fractional part of numeric_expression is rounded before the string is created.

If digits is specified, the result string will be of length digits. digits may be in the range 1 to 8. If the value is shorter than digits, leading zeros are appended as needed. If the value is longer than digits, the result will be truncated from the left as necessary.

Hexadecimal is a number system that uses base 16 rather than the base 10 used by the everyday decimal system. In hexadecimal, the digits 0 to 9 represent the same numbers as in decimal, and the letters A to F represent the decimal values 10 to 15. Hexadecimal notation is commonly used in programming because it is a convenient way of representing the binary bit patterns used internally by computers. A single hex digit represents 1 nibble (4 bits), two hex digits represent one byte (8 bits), and so forth.

By convention, hex numbers are unsigned, since they represent bit patterns. If you are not familiar with two's-complement arithmetic, the result of using HEX$ on a negative (signed) argument may surprise you. See Numeric Literals for more information on explicitly casting numeric literal values to specific and distinguishing between signed and unsigned values. Also see the BIN$ function.

To display a Quad-integer in hex format, use the following technique:

DIM q AS QUAD, r AS STRING

q = &H0ABCEF0EE1234567F&&

r = HEX$(q / &H100000000, 8) & HEX$(q AND &HFFFFFFFF, 8)

Hexadecimal strings can be converted to numeric values with the VAL function by prefixing the hex string with "&H". If the string has a leading zero, the result is always unsigned. For example:

a$ = HEX$(65535) ' a$ will contain "FFFF"

x& = VAL("&H" + a$) ' Signed result (-1)

y& = VAL("&H0" + a$) ' Unsigned result (65535)

See also

BIN$, FORMAT$, OCT$, STR$, USING$, VAL

Example

DIM a AS DWORD, b AS STRING, c AS QUAD

a = &H0FFFFFFFF ' Unsigned literal

b = HEX$(a) ' "FFFFFFFF"

c = a ' 4294967295

b = HEX$(c) ' "FFFFFFFF"

c = VAL("&H" + b) ' -1&& (signed conversion)

b = HEX$(c) ' "FFFFFFFF"

c = VAL("&H0" + b) ' 4294967295&&

b = HEX$(c) ' "FFFFFFFF"


HI function

Purpose

Extract the most significant (high-order) portion of an value.

Syntax

result = HI(type, value)

Remarks

The value returned by HI is unsigned if type is BYTE, WORD, or DWORD, and signed if type is INTEGER or LONG. value may be up to twice the size of the data type specified by type. In the following example, n may be up to a 16-bit value (twice the size of a BYTE):

b = HI(BYTE,n)

Restrictions

HI replaces HIBYT, HIWRD, and HIINT. Note that those functions may not be supported in future versions of PowerBASIC, so update your code to use the new syntax.

See also

LO, MAK


HIBYT function

Purpose

Extract the most significant (high-order) byte from an Integer or Word value, and return it as an unsigned Byte value

HIBYT has been superceded by the HI function, although HIBYT remains supported for a limited period. Existing code should be converted to the new syntax as soon as possible.

Syntax

bResult? = HIBYT(sixteenbitvalue)

Remarks

The value returned by HIBYT is always unsigned, regardless of the sign of the argument. Effectively, this function provides the same result as PEEKing at the second byte of the argument. Thus, when HIBYT is used on a Long-integer or Double-word, the result is actually the high byte of the low Word of the value.

See also

HI, HIINT, HIWRD, LO, LOBYT, LOINT, LOWRD, MAKDWD, MAKINT, MAKLNG, MAKPTR, MAKWRD


HIINT function

Purpose

Extract the most significant (high-order) Word from a Long-integer or Double-word (DWORD) value, and return it as a signed Integer value.

HIINT has been superceded by the HI function, although HIINT remains supported for a limited period. Existing code should be converted to the new syntax as soon as possible.

Syntax

iResult% = HIINT(thirtytwobitvalue)

Remarks

The value return by HIINT is always signed, regardless of the sign of the argument.

See also

HI


HIWRD function

Purpose

Extract the most significant (high-order) Word from a Long-integer or Double-word (DWORD) value and return it as an unsigned Word value.

HIWRD has been superceded by the HI function, although HIWRD remains supported for a limited period. Existing code should be converted to the new syntax as soon as possible.

Syntax

wResult?? = HIWRD(thirtytwobitvalue)

Remarks

The value return by HIWRD is always unsigned, regardless of the sign of the argument.

See also

HI


HOST ADDR statement

Purpose

Translate a host name into a corresponding IP address.

Syntax

HOST ADDR [hostname$] TO ip&

HOST ADDR(index&) TO ip&

Remarks

hostname$ is the name of a computer on the network or a domain name such as "powerbasic.com". If hostname$ is zero-length or not specified, the primary IP address of the current computer is returned.

ip& receives the IP address of the specified host name. ip& may be a REGISTER or memory variable.

It is possible for a computer to have more than one IP address. For example, if you have a network card in your computer, and you are dialed into the Internet using a modem, your computer will have two IP addresses. By using the indexed form of the statement:

HOST ADDR(index&) TO ip&

…you can retrieve the first IP address with index& = 1, the second with index& = 2, etc. If, on return, ip& contains zero (0), there are no further IP addresses to retrieve on that computer.

A numeric IP address can be easily converted to a dotted IP address with the following code:

DIM p AS BYTE PTR

HOST ADDR "localhost" TO ip&

p = VARPTR(ip&)

a$ = USING$("#_.#_.#_.#", @p, @p[1], @p[2], @p[3])

' returns "127.0.0.1"

Restrictions

In order to obtain the IP address of the current computer, you must have at least one socket open, or you must first obtain the name of the computer by using the HOST NAME statement.

See also

HOST NAME, TCP and UDP Communications, TCP OPEN, UDP OPEN

Example

HOST ADDR "powerbasic.com" TO ip& ' Primary IP

FUNCTION HowManyIPs() AS LONG

DIM p AS BYTE PTR

RESET index&

DO

HOST ADDR(index&+1) TO ip&

IF ISTRUE ip& THEN

INCR index&

p = VARPTR(ip&)

a$ = USING$("#_.#_.#_.#", @p, @p[1], @p[2], @p[3])

END IF

LOOP UNTIL ip& = 0

FUNCTION = index&

END FUNCTION


HOST NAME statement

Purpose

Translate an IP address into a corresponding host name.

Syntax

HOST NAME [ip&] TO hostname$

Remarks

ip& is the IP address you want to look up. If ip& is zero (0) or not specified, the name of the current computer is returned. hostname$ receives the name of the host corresponding to the IP address.

In order to translate an IP address into an Internet domain name, your computer will need to be connected to a DNS server on the Internet or local Intranet.

See also

HOST ADDR, TCP and UDP Communications, TCP OPEN, UDP OPEN

Example

HOST ADDR "powerbasic.com" TO ip&

HOST NAME ip& TO hostname$

CALL ShowResult(hostname$, ip&)


IF/END IF block

Purpose

Create IF/THEN/ELSE constructs with multiple lines and/or conditions.

Syntax

IF integer_expression THEN

{statements}

[ELSEIF integer_expression THEN

{statements}]

[ELSE

{statements}]

END IF

Remarks

In executing IF blocks, the truth of the integer_expression in the initial IF statement is checked first. If it evaluates to FALSE (zero), each of the following ELSEIF statements is examined in order. There can be as many ELSEIF statements as desired. As soon as one is found to be TRUE (non-zero), PowerBASIC executes the statement(s) following the associated THEN and before the next ELSEIF or ELSE.

Execution then jumps to the statement just after the terminating END IF without making any further tests. If none of the test expressions evaluates to TRUE, the statement(s) in the ELSE clause (which is optional) are executed.

Note that there must be nothing following the THEN keyword in an IF block; that's how the compiler distinguishes an IF block from a conventional IF statement. There must also be nothing on the same line as the ELSE (except for remarks).

IF blocks can be nested; that is, any of the statements after any of the THENs may contain IF blocks. Although the compiler doesn't care, the clarity of source code is improved by indenting the statements controlled by each test a couple of spaces, as shown in the example.

IF blocks must be terminated with a matching END IF statement. Note that the END IF statement requires a space and the ELSEIF statement does not.

See the IF statement for notes on PowerBASIC's Short-circuit evaluation and its possible side effects.

See also

CHOOSE, CHOOSE&, CHOOSE$, EXIT, IF, IIF, IIF&, IIF$, MAX, MAX&, MAX$, MIN, MIN&, MIN$, SELECT, Short-circuit evaluation, SWITCH, SWITCH&, SWITCH$, SELECT

Example

X = (RND * 500) + 1

IF X = 1 THEN

x$ = "The number is 1"

ELSEIF X = 2 THEN

x$ = "The number is 2"

ELSE

IF X < 50 THEN

x$ = "The number is less than 50"

ELSEIF X < 100 THEN

x$ = "Greater than 49 and less than 100"

ELSE

x$ = "The number is 100 or greater"

END IF

END IF


IF statement

Purpose

Test a condition and execute one or more program statements only if the condition is met.

Syntax

IF integer_expression THEN {sub | label | {statements} [ELSE {sub | label | {statements}]

Remarks

If integer_expression is TRUE (evaluates to a non-zero value), the statements following THEN are executed, and the statements following the optional ELSE are not executed. If integer_expression is FALSE (zero), the statements following THEN are not executed, and the statements following the optional ELSE are executed. If the ELSE clause is omitted, execution continues with the next line of the program, provided integer_expression evaluates to FALSE.

integer_expression will often be a result returned by a relational operator as shown here:

IF Income > Expenses THEN x$ = "OK!" ELSE x$ = "Uh-oh"

integer_expression can also be a boolean value. For example, your program could set the variable BeepOn to 1 (or any non-zero value) if audible beeps are requested, and to 0 if not, then use an IF statement to control output:

IF BeepOn THEN BEEP

…is equivalent to:

IF BeepOn <> 0 THEN BEEP

integer_expression can include the logical operators AND and OR, as in:

IF (a = b) AND (c = d) THEN x$ = "They are equal"

label

If a label is specified, the label must appear within the same Sub or Function as the IF statement. The GOTO keyword is implied by THEN, or can replace THEN:

IF EOF(1) THEN GotFile

IF EOF(1) GOTO GotFile

proc

If proc is specified, it must identify a Sub or Function.

The IF statement and all its associated statements, including those after an ELSE, must appear on the same logical program line. The following is therefore illegal:

IF a < b THEN t = 15 : u = 16 : v = 17

ELSE t = 17 : u = 16 : v = 15

…because the compiler treats the ELSE statement as a brand-new statement unrelated to the one above it. If you have more statements than you can fit on one line, you can use the line continuation character, the underscore "_", to spread a single logical line over several physical lines. For example, the following is a legal way of restating the last example:

IF a < b THEN t = 15 : u = 16 : v = 17 _

ELSE t = 17 : u = 16 : v = 15

A better method of programming long and complex IF/THEN constructs is to use the IF block statement.

Also note that every statement following the ELSE will be executed if integer_expression is FALSE. For example, you might expect the following statement:

Taxable = %TRUE

Price = 1.00

Rate = .05

Total = 5.00

IF Taxable THEN Tax = Price * Rate ELSE Tax = 0: Total = Total + Tax

…to bring Total to 5.05, but it won't. The Total = Total + Tax statement will only be executed if Taxable is FALSE. It's easy to get the correct results using the IF block:

IF Taxable THEN

Tax = Price * Rate

ELSE

Tax = 0

END IF

Total = Total + Tax

Short-Circuit Evaluation

Note that PowerBASIC features short-circuit evaluation of relational expressions using AND and OR. This optimization means that evaluation of a relational expression in an IF, IF/END IF, DO/LOOP, or WHILE/WEND is terminated just as soon as it is possible to tell what the result will be. For example:

IF LEN(a$) AND MyFunc(a$) THEN CALL ShowText("Ok!")

In the above example, if LEN(a$) is zero, there is no further need to evaluate the expression, because 0 and anything will always be FALSE. So, if LEN(a$) is zero, MyFunc() is not called at all, and ShowText() is not executed.

To give short-circuit optimization an extra boost, AND and OR are treated as a Boolean operator rather than a bitwise operator, and this can sometimes produce unexpected results. For example, consider the following expression:

a& = 4

b& = 2

IF a& AND b& THEN CALL ShowText("TRUE") ELSE CALL ShowText("FALSE")

Applying the traditional BASIC bitwise evaluation, you would expect to see FALSE displayed because (4 AND 2) = 0. Due to the short-circuit optimization though, each value is treated as a Boolean, just as if you had written:

IF ISTRUE a& AND ISTRUE b& THEN ...

If you believe this may be a problem for your particular code, you can disable the short-circuit evaluation by surrounding the entire conditional expression in parentheses:

IF (a& AND b&) THEN CALL ShowText("TRUE") ELSE CALL ShowText("FALSE")

The parentheses force the entire expression to be evaluated, so AND reverts to being a bitwise operator.

See also

CHOOSE, CHOOSE&, CHOOSE$, IF block, IIF, IIF&, IIF$, MAX, MAX&, MAX$, MIN, MIN&, MIN$, SWITCH, SWITCH&, SWITCH$, SELECT

Example

IF x > 100 THEN y = 3 ELSE y = 4


IIF function

Purpose

Return one of two values based upon a TRUE/FALSE evaluation.

Syntax

var = IIF(num_expression, truepart, falsepart)

var& = IIF&(num_expression, truepart&, falsepart&)

var$ = IIF$(num_expression, truepart$, falsepart$)

Remarks

IIF expects parts of any numeric type. IIF& expects parts optimized for long integer type. IIF$ expects parts of type.

If num_expression evaluates to TRUE (non-zero), the truepart is returned, else the falsepart is returned. num_expression is evaluated as a normal PowerBASIC Boolean expression, which offers short-circuit expression evaluation as needed.

IIF(1 AND 2, 3, 4) would return the truepart (3) because both terms in num_expression are TRUE, and therefore evaluate to TRUE.

To force a bitwise evaluation of num_expression, enclose it in parentheses. For example, IIF$( (1 AND 2), "True", "False") would return "False".

IIF% is recognized as a valid synonym for IIF&.

Restrictions

Contrary to the implementation in some other languages, only the selected expression (truepart or falsepart) is evaluated at run-time, not both. This ensures optimum execution speed, as well as the elimination of unanticipated side effects.

See also

CHOOSE, CHOOSE&, CHOOSE$, SWITCH, SWITCH&, SWITCH$

Example

iLOGFONT.lfWeight = IIF&(Weight&, 700&, 400&)

Score& = Score& + IIF&(Answer = %FALSE, 0, 10)


IMP operator

Purpose

The IMP operator works as both a logical and a bitwise arithmetic operator.

Syntax

p IMP q

Remarks

IMP as a logical operator

The IMP operator returns FALSE (zero) if and only if its first operand is TRUE (non-zero), and its second operand is FALSE. In all other cases, it returns TRUE.

Truth table
x | y | x IMP y
T | T | T
T | F | F
F | T | T
F | F | T

Using IMP as a bitwise arithmetic operator

IMP is seldom used as a bitwise arithmetic operator, but here is a sample:

See also

Arithmetic Operators, AND, EQV, ISFALSE, ISTRUE, NOT, OR, XOR


INCR statement

Purpose

Increment a variable by 1; increment a pointer by the size of its target; or increment the target of a numeric by 1.

Syntax

INCR variable

Remarks

variable can be a or a pointer variable. When INCR is used with a numeric variable, 1 is added to the numeric variable.

When INCR is used with a pointer variable itself, the value of the pointer is incremented by the size of the pointer's target.

When INCR is used on a numeric pointer's target (i.e., INCR @IntPtr) the value of the target is incremented by 1.

For example, given a pointer to an array where the pointer targets element 1000, applying INCR to the pointer would result in the pointer aiming at element 1001. The actual address held by the pointer would have risen by two, because the target of the pointer is an Integer which is two bytes wide.

Conversely, if INCR was used on the target of this Integer pointer, the value of the element itself would be incremented by 1.

See also

DECR

Example

DIM x&, LongPtr AS LONG POINTER

INCR x&

INCR LongPtr

INCR @LongPtr


INKEY$ function

Purpose

Read a keyboard character or mouse event from the console input buffer, without echoing it to the screen. INKEY$ will return immediately, even if there are no character or mouse events to retrieve.

Syntax

i$ = INKEY$

Remarks

INKEY$ returns a of 0, 1, or 2 characters that reflects the status of the keyboard buffer. It can also return a string of four bytes if mouse event trapping is enabled. INKEY$ will always read keys from the keyboard, regardless of STDIN redirection status.

A null string (LEN(i$) = 0) means that the buffer is empty (no key or mouse event is available).

A string length of one (LEN(i$) = 1) means that an ASCII key was pressed and the string contains the ASCII character. Values between 1 and 31 indicate a control code.

A string length of two (LEN(i$) = 2) means that an extended key was pressed. In this case, the first character in the string has a value of zero, and the second is the extended keyboard code. For example, pressing the F1 key will return CHR$(0,59).

A string length of four (LEN(i$) = 4) means that a mouse event was retrieved. In this case, the first two characters in the string have a value of 255. The third character is an event code:

Value | Definition
1 | movement
2 | double-click
4 | button press
8 | button release

The fourth character defines the button that was pressed:

Value | Definition
0 | no button
1 | left
2 | right
4 | second left button
8 | third left button
16 | fourth left button

For example, moving the mouse with the left button pressed will return CHR$(255, 255, 1, 1). Button presses can be combined, for example pressing the left and right mouse buttons together will return CHR$(255, 255, 4, 3). The Mouse must be enabled with the MOUSE statement in order to receive mouse events.

If you are converting DOS code to PB/CC, you are likely to come across the following type of loop:

WHILE NOT INSTAT : WEND

a$ = INKEY$

or:

DO

a$ = INKEY$

LOOP UNTIL LEN(a$)

While both of these loops work fine in PB/CC, they take as much CPU attention as possible. As a result, this type of loop is very wasteful and can cause other applications to work sluggishly, even when the PB/CC app is running in the background. The solution is to add a SLEEP 0 statement within the loop (to release the current CPU time-slice to other tasks) or, better, use the WAITKEY$ function instead.

See also

INPUT FLUSH, INSHIFT, INSTAT, LINE INPUT, MOUSE, MOUSESTAT, MOUSEX, MOUSEY, WAITKEY$, WAITSTAT


INPUT FLUSH statement

Purpose | Empty all pending keyboard and mouse events from the console input buffer.
Syntax | INPUT FLUSH
See also | INKEY$, INSHIFT, INSTAT, WAITKEY$, WAITSTAT
Example | PRINT "Press any key to continue…" INPUT FLUSH WAITKEY$


INPUT statement

Purpose

Read a line from the keyboard, assigning data to one or more variables.

Syntax

INPUT [prompt,] variable_list

Remarks

prompt is a string literal or equate that is displayed to the user as a prompt. variable_list is a comma-delimited sequence of one or more or variables. Leading spaces ($SPC) are removed from string data.

If a single INPUT statement prompts for more than one variable, the user must enter the proper number of values on a single line, separated by commas. If not enough comma-delimited values are entered, remaining variables are set to zero (if numeric) or empty (if string).

Restrictions

INPUT supports fixed-length and ASCIIZ string variables; input data will be truncated as necessary to fit into such strings. Dynamic strings receive the data without truncation. UDT variables may not be used, although fixed-length and ASCIIZ UDT member variables are supported. This statement always returns keyboard input. It is not subject to input redirection.

See also

INKEY$, INSHIFT, INSTAT, INPUT#, LINE INPUT, WAITKEY$

Example

StartHere:

CLS

PRINT "Please input your name, then type a comma,"

INPUT "and state your age, using digits: ", var1$, var2&

CLS

IF LEN(var1$) = 0 THEN ' no name given

PRINT "You need to input your name."

ELSEIF var2& = 0 THEN ' no age given, or missing comma

PRINT "Hi "; var1$

PRINT "Please input your name, a comma, and age."

ELSEIF var2& > 51 THEN

PRINT "Hi "; var1$

PRINT "Are you really"; var2&; "years young?"

ELSE

PRINT "Hi "; var1$

PRINT "Are you really"; var2&; "years old?"

END IF

PRINT "Press Esc to exit, or any other key to repeat."

IF WAITKEY$ <> $ESC THEN GOTO StartHere ' exit on Esc key


INPUT# statement

Purpose

Load variables with data from a sequential file.

Syntax

INPUT #filenum&, variable_list

Remarks

filenum& is the file number, or variable containing a file number, given when the file was opened. variable_list is a comma-delimited sequence of one or more or variables. When the INPUT# statement reads an unquoted data item from a file, it removes leading and trailing "white space" (spaces, tabs, and linefeeds). If white space is significant, place quotes around the file data, either directly or by using WRITE# to save the data to disk. Please note that data to be quoted should not contain embedded quotes.

The data in the file must match the type(s) of the variable(s) defined in the INPUT# statement. The file data should be separated by commas with a carriage return at the end. The WRITE# statement is ideal for creating such files.

INPUT# also supports fixed-length and ASCIIZ string variables; however, data that is longer than the string is truncated to fit into the string. Dynamic strings receive the data without truncation. UDT variables may not be used, although fixed-length and ASCIIZ UDT member variables are supported.

See also

LINE INPUT#, PRINT#, WRITE#

Example

SUB MakeFile

' MakeFile opens a sequential file for output.

' Using WRITE#, it writes lines of different

' data types to 'the file.

OPEN "INPUT#.DTA" FOR OUTPUT AS #1

StringVariable$ = "I'll be back."

IntegerVar% = 1000

FloatingPoint! = 30000.12

' Write a line of text to the sequential file.

WRITE #1, StringVariable$, IntegerVar%, FloatingPoint!

CLOSE #1

END SUB

SUB ReadFile

' This procedure opens a sequential file for

' input. Using INPUT# it reads lines of

' different data types from the file.

OPEN "INPUT#.DTA" FOR INPUT AS #1

RESET StringVariable$

RESET IntegerVar%

RESET FloatingPoint!

' Read a line of text from the sequential file.

INPUT #1, StringVariable$, IntegerVar%, FloatingPoint!

CLOSE #1

END SUB


INSHIFT function

Purpose

Return the state of the keyboard Shift, Ctrl and/or Alt keys, as of the last keystroke or mouse event retrieved by INKEY$ or WAITKEY$.

Syntax

status% = INSHIFT

Remarks

The status% variable receives a bitmask with the shift status:

status% | Definition
1 | Right-Alt
2 | Left-Alt
4 | Right-Ctrl
8 | Left-Ctrl
16 | Shift
32 | Numlock
64 | Scroll Lock
128 | Caps Lock
256 | Enhanced Key

Values can be combined, for example status% = 17 if the right-alt and shift keys were both active.

See also

BIT function, INKEY$, WAITKEY$, WAITSTAT


INSTAT function

Purpose

Determine whether a keyboard or mouse event is ready to be retrieved from the console input buffer.

Syntax

x = INSTAT

Remarks

The x variable receives the keyboard buffer status. INSTAT return logical TRUE (non-zero) if a keyboard or mouse event is ready to be retrieved from the console input buffer, or FALSE (zero) if there are no events in the buffer.

INSTAT does not remove a keyboard or mouse event from the buffer. So if INSTAT ever returns TRUE, it will always return TRUE until the event is removed from the buffer using INKEY$ or WAITKEY$. Mouse support must be enabled in order to test for mouse events.

INSTAT is not subject to redirection.

See also

INKEY$, INSHIFT, MOUSE, WAITKEY$, WAITSTAT


INSTR function

Purpose

Search a for the first occurrence of a specified character or string.

Syntax

y& = INSTR([n&,] MainString, [ANY] MatchString)

Remarks

INSTR returns the position of MatchString within MainString.

n& is an optional Long-integer expression, and MainString and MatchString are string variables, expressions, or string equates/constants.

If n& is greater than zero (positive), MainString is searched from left to right, starting at character position n&, for the first occurrence of MatchString.

If n& is less than zero (negative), MainString is searched from right to left for the first occurrence of MatchString. If -1, the search begins at the last position. If -2, the search begins at the second to last position, and so forth.

x& = INSTR(-2, "efcdef", ANY "ef")

This returns 5.

If MatchString is not found in the specified portion of MainString, or n& evaluates outside of MatchString, INSTR returns 0. If MatchString is null (an empty string), INSTR returns 1 (if n& not specified) or n& (if n& is specified).

ANY

If the ANY keyword is included, MatchString specifies a list of single characters to be searched for individually, a match on any one of which will conclude the search operation, and return the position of the match.

INSTR is case-sensitive, meaning that upper-case and lower-case letters must match exactly in MatchString and MainString.

Restrictions

If n& is zero, INSTR will return zero.

See also

EXTRACT$, LCASE$, LEFT$, LTRIM$, MID$, RIGHT$, RTRIM$, TALLY, TRIM$, UCASE$, VERIFY

Example

' x$ = first command-line argument, assuming

' spaces, commas, periods, and tabs are valid

' delimiters

IF INSTR(COMMAND$, ANY " ,." + CHR$(9)) > 0 THEN

x$ = "There is more than one command-line argument"

ELSE

x$ = "There is at most one command-line argument"

END IF


INT function

Purpose

Convert a numeric expression to an value.

Syntax

y = INT(numeric_expression)

Remarks

INT rounds numeric_expression to the largest integer-class value that is less than or equal to numeric_expression.

See also

CEIL, CINT, FIX, FRAC, ROUND

Example

DIM X AS SINGLE, Y AS LONG

FOR X = -1.1 TO 2.1 STEP .5

Y = INT(X)

NEXT X

Result

X Y

-1.1 -2

-0.6 -1

-0.1 -1

0.4 0

0.9 0

1.4 1

1.9 1


INTERFACE/END INTERFACE block

Purpose

Declare a dispatch interface and its member Methods/Properties for the purposes of early-binding to a COM interface.

Syntax

INTERFACE DISPATCH interfacename

MEMBER {CALL | GET | SET | LET} membername <dispid> ( [[OPTIONAL

[IN | OUT | INOUT]] paramname <dispid> [AS type] [,...]] )

[AS {vartype | interface}]

[...]

END INTERFACE

Remarks

In order to provide early-binding services, PowerBASIC must be able to pre-construct the references to the COM interface members at compile-time. Without an interface definition block, only late-binding at run-time would be possible. Late-binding is less efficient than early-binding.

You may list every Method/Property in the interface, or just the ones that are referenced in the code. They can appear in any sequence. Member names may contain (normally) reserved keywords such as INPUT or KILL, etc

The most important aspect of an interface block is that it clearly associates a dispid with the each Method/Property name. Named parameters in the paramname list also require an appropriate dispid value, as does any Property which returns an object to be used in a nested object reference. All dispid values must be enclosed in angle brackets (< and >), and may be expressed as hexadecimal or decimal numeric literals.

You can look up the dispid values of COM servers using an Object Browser, or by reading your object documentation. You can even insert additional information about the types and return value for your own reference, even though the compiler does not use them.

Restrictions

If the compiler cannot resolve the interface name definition specified in a DIM, LET, or SET statement, a compile-time error is generated accordingly.

interfacename must not be a PowerBASIC keyword. If a keyword conflict arises, the addition of an arbitrary prefix is acceptable. For example, INTERFACE DISPATCH Shell() could be changed to INTERFACE DISPATCH MyShell() and PowerBASIC will still resolve the interface correctly.

Method/Property membername items may freely use PowerBASIC keywords without concern for conflicts with normal code syntax. For example, MEMBER CALL Open() is a valid syntax for an interface method.

See also

DIM, Members, Methods and Properties, OBJACTIVE, OBJECT, OBJPTR, OBJRESULT, Parameters, PROGID$, Return Data, SET

Example

INTERFACE DISPATCH IAPPUser

MEMBER CALL DELETE<&H1>()

MEMBER GET Name<&H2>() AS STRING

MEMBER LET Name<&H2>() 'Param Type As String

MEMBER LET Password<&H3>() 'Param Type As String

MEMBER GET ReadOnly<&H4>() AS LONG

MEMBER LET ReadOnly<&H4>() 'Param Type As Long

MEMBER GET ProjectRights<&H5>(OPTIONAL IN Project AS STRING<&H0>) AS LONG

MEMBER LET ProjectRights<&H5>(OPTIONAL IN Project AS STRING<&H0>)

MEMBER CALL RemoveProjectRights<&H6>(IN Project AS STRING<&H0>)

END INTERFACE

INTERFACE DISPATCH IAPPItems

MEMBER GET Count<&H1>() AS LONG

MEMBER GET Item<&H0>(IN sItem AS VARIANT<&H0>) AS IAPPItem

END INTERFACE

DIM oApp AS IAPPUser

LET oApp = NEW IAPPUser IN "com.server.0"


ISFALSE and ISTRUE operators

Purpose

Return the logical truth or falsity of a given expression.

Syntax

ISFALSE expr

ISTRUE expr

Remarks

ISTRUE returns -1 (TRUE) when expr evaluates as non-zero; otherwise, it returns zero (FALSE). ISFALSE returns -1 when expr evaluates as 0 (FALSE); otherwise, it returns zero.

Truth table
operator | expr | Result
ISTRUE | = 0 | 0
ISTRUE | <> 0 | -1
ISFALSE | = 0 | -1
ISFALSE | <> 0 | 0

PowerBASIC's NOT operator serves a double duty: it returns the one's-complement of an expression and "reverses" the value of a TRUE/FALSE (Boolean) expression. Usually, these two functions do not conflict, but since PowerBASIC accepts any non-zero value as TRUE, the following condition can arise:

test1 = 0 ' test1 is FALSE (zero)

IF NOT test1 THEN ' TRUE (-1 is non-zero)

...

test2 = 1 ' test2 is TRUE (1 is non-zero)

IF NOT test2 THEN ' still TRUE (-2 is non-zero)

...

In this case, NOT does not reverse the TRUE/FALSE value of test2. ISFALSE ensures that the test is performed exactly as you would expect:
test2 = 1 ' test2 is TRUE (non-zero)

IF ISFALSE test2 THEN ' ISFALSE detects test2 is

... ' TRUE so the IF test fails

This problem does not exist when you're testing for logical truth. PowerBASIC considers that an expression is TRUE in every case except when the expression is zero. However, ISTRUE converts all non-zero values to the "most true" value, -1, which provides the most consistent results with both boolean and arithmetic expressions.

Restrictions

ISTRUE and ISFALSE operators evaluate the "whole" expression following the keyword, subject to their Operator Precedence level. For example, parentheses contained within the expression are regarded as an integral part of the expression, and do not act as delimiters for the ISTRUE and ISFALSE operators.

With this in mind, combining a logical test result into a further expression means that the expressions must be separated to ensure the correct evaluation.

Consider the following statement:

IF ISTRUE (x&) + y& THEN

PowerBASIC evaluates the entire expression (x&) + y& and then calculates the logical truth from the overall result of that expression. That is, the parentheses around the first part of the expression do not stop ISTRUE from evaluating the whole expression. To demonstrate this, the statement can be rewritten to concisely demonstrate the scope of the logical evaluation:

IF ISTRUE (x& + 2) THEN

or it could be simplified even further:

IF ISTRUE x& + 2 THEN

If you wish to utilize the numeric result of the logical test in a further expression, parentheses must be added to separate the expressions correctly:

IF (ISTRUE x&) + 2 THEN

See also

Arithmetic Operators, NOT, Short-circuit evaluation


ISNOTHING function

Purpose | Determine the current status of a given object variable.
Syntax | oStatus = ISNOTHING(objectvar)
Remarks | ISNOTHING is particularly useful is determining the success or failure of a LET or SET statement. It returns TRUE (-1) if the object variable contains nothing, or FALSE (0) if it contains a valid current reference to an object interface. ISNOTHING is the complement to the ISOBJECT function.
Restrictions | objectvar must be an Interface or Dispatch object variable.
See also | DIM, INTERFACE/END INTERFACE, ISOBJECT, OBJECT, SET
Example | DIM oApp AS IAPPDatabase LET oApp = NEW IAPPDatabase IN "DBApp.0" IF ISNOTHING(oApp) OR ERR THEN ...


ISOBJECT function

Purpose | Determine the current status of a given object variable.
Syntax | oStatus = ISOBJECT(objectvar)
Remarks | ISOBJECT is particularly useful is determining the success or failure of a LET or SET statement. It returns TRUE (-1) if the object variable contains a valid current reference to an object interface, or FALSE (0) if it contains nothing. ISOBJECT is the complement to the ISNOTHING function.
Restrictions | objectvar must be an Interface or Dispatch object variable.
See also | DIM, INTERFACE/END INTERFACE, ISNOTHING, OBJECT, SET
Example | DIM oApp AS IAPPDatabase LET oApp = NEW IAPPDatabase IN "DBApp.0" IF ISOBJECT(oApp) AND ISFALSE ERR THEN ...


ITERATE statement

Purpose

Start an immediate iteration of a structure.

Syntax

ITERATE [DO | LOOP | FOR]

Remarks

ITERATE is just like using a GOTO to the line immediately before the NEXT statement (of a FOR...NEXT loop), the LOOP statement (of a DO...LOOP loop), or the WEND statement (of a WHILE..WEND loop). For example, the following code fragments are equivalent:

FOR ix = 1 TO 100

...

ITERATE FOR

...

NEXT

FOR ix = 1 TO 100

...

GOTO iterateForLoop

...

iterateForLoop:

NEXT

If you do not specify DO, LOOP, or FOR, ITERATE will iterate the most recently executed structure. For example:

FOR ix = 1 TO 10

DO UNTIL x > 10

...

ITERATE ' will iterate the DO LOOP

...

LOOP

NEXT

ITERATE DO and ITERATE LOOP are interchangeable.

Use this statement... | To iterate this kind of loop
ITERATE FOR | FOR/NEXT
ITERATE DO, ITERATE LOOP | DO/LOOP, WHILE/WEND

See also

DO/LOOP, EXIT, FOR/NEXT, WHILE/WEND


JOIN$ function

Purpose

Return a consisting of all of the strings in a string array, each separated by a delimiter.

Syntax

A$ = JOIN$(array$(), {delim$ | BINARY})

Remarks

JOIN$ requires a delimiter string delim$ which may be any length.

If the delimiter expression is a null (zero-length) string, no separators are inserted between the string sections. If the delimiter expression is the 3-byte value of "," (which may be expressed in your source code as the string literal ""","""), a leading and trailing double-quote is added to each string section. This ensures that the returned string contains standard, comma-delimited quoted fields that can be easily parsed.

The array specified by array$() may be a dynamic string array, a fixed-length string array, an ASCIIZ string array, or a .

BINARY

If the array consists of fixed size elements ( , ASCIIZ, etc.), the returned string consists of an exact memory image of the array data in internal format. If the array contains variable length data (Dynamic string, Field string), it is stored in PowerBASIC and/or Visual Basic packed string format: If a string is shorter than 65535 bytes, it starts with a 2-byte length WORD followed by the string data. Otherwise it will start a 2-byte value of 65535, followed by a DWORD indicating the string length, then finally the string data itself.

The JOIN$ function is the natural complement to the PARSE statement.

See also

PARSE, PARSE$, PARSECOUNT

Example

FUNCTION PBMAIN

DIM a$(2), s1$, s2$

a$(0) = "Hello"

a$(1) = "Power"

a$(2) = "BASIC"

s1$ = JOIN$(a$(),""",""")

s2$ = JOIN$(a$(),$SPC)

END FUNCTION

Result

s1$ contains: "Hello","Power","BASIC"

s2$ contains: Hello Power BASIC


KEY$ function

Purpose | Retrieve the string expression assigned to a function key with the KEY statement.
Syntax | keyexpr$ = KEY$(keynum&)
Remarks | The expression keyexpr$ is given the value assigned to the function key specified by keynum& (a number from 1 to 12). For compatibility with DOS compilers, a keynum& of 30 or 31 is translated to 11 or 12.
Example | FUNCTION PBMAIN KEY 5, "Compile" KEY 6, "without" KEY 7, "compromise!" PRINT KEY$(5), KEY$(6), KEY$(7) WAITKEY$ END FUNCTION
See also | KEY


KEY statement

Purpose

Assign a string expression to a function key (F1 to F12).

Syntax

KEY keynum&, keyexpr$

Remarks

When KEY is executed, the expression keyexpr$ (up to 15 characters) is assigned to the function key specified by keynum& (a number from 1 to 12). For compatibility with DOS compilers, a keynum& of 30 or 31 is translated to 11 or 12.

When the designated function key is pressed, the string expression is sent to the keyboard input stream.

Example

FUNCTION PBMAIN

KEY 5, "PowerBASIC" + $CR

INPUT "Please press F5: ", var1$

PRINT "F5 Keyexpr$ was: " + var1$

WAITKEY$

END FUNCTION

See also

KEY$


KILL statement

Purpose

Delete a disk file.

Syntax

KILL filespec

Remarks

filespec is a string expression specifying the file or files to be deleted, and can include a path name and/or "wildcard" characters. filespec may be either a Short File Name (SFN) or a Long File Name (LFN). For example:

KILL "TEST.DOC"

KILL "C:\MY APPLICATION DATA\INCOME.?87"

MyFile$ = "*.BAS"

KILL MyFile$ ' Potentially dangerous!

If filespec does not exist, Error 53 ("File not found") is generated. If filespec is read only, Error 70 ("Permission denied") occurs. You should not attempt to KILL an open file.

Files with the HIDDEN or SYSTEM attribute can not be deleted with KILL. An attempt to do so is ignored, with no error generated.

KILL is analogous to the DOS "DEL" and "ERASE" commands. KILL cannot delete a directory - use RMDIR instead, after first deleting all the files in the directory.

See also

FILEATTR, FILECOPY, FILENAME$, GETATTR, NAME, RMDIR, SETATTR, SETEOF


LBOUND function

Purpose | Return the smallest possible subscript (boundary) for an array's specified dimension.
Syntax | y& = LBOUND(array [(dimension)]) y& = LBOUND(array, dimension)
Remarks | LBOUND can be used in combination with UBOUND to determine the size of an array. LBOUND of an undimensioned array returns zero. The LBOUND function has the following parts:
array | Name of the array of interest.
dimension | An integer indicating which dimension's lower bound is returned. If not specified, the first dimension is assumed.
Restrictions | LBOUND cannot be used on arrays within User-Defined Types.
See also | ARRAYATTR, DIM, REDIM, UBOUND
Example | ' Dimension an array with lower and upper bounds DIM MyArray%(1900 TO 2000,5 TO 10) ' get the values of the array l1 = LBOUND(MyArray%) u2 = UBOUND(MyArray%) l2 = LBOUND(MyArray%(2)) u2 = UBOUND(MyArray%, 2)


LCASE$ function

Purpose

Return a lowercase version of a argument.

Syntax

s$ = LCASE$(string_expression [,ANSI | OEM])

Remarks

LCASE$ returns a string equal to string_expression, except that all the uppercase letters in string_expression are converted to lowercase. The optional ANSI or OEM parameter specifies whether string_expression is converted to lowercase using the ANSI or OEM charset. If no charset option is selected, then the Console’s current OEM charset is used to convert string_expression to lowercase. The PowerBASIC Compiler for Windows defaults to using the systems ANSI charset to convert string_expression. Only "International" characters in the range CHR$(128) to CHR$(255) are affected by the charset parameter.

See also

MCASE$, UCASE$

Example

x$ = LCASE$("Cats aren't ALWAYS good.")

Result

cats aren't always good.


LEFT$ function

Purpose

Return the left-most n characters of a .

Syntax

s$ = LEFT$(string_expression, n&)

Remarks

n& is a Long-integer expression and specifies the number of characters in string_expression to be returned.

LEFT$ returns a string consisting of the left most n& characters of its string argument. If n& is greater than or equal to the length of string_expression, all of string_expression is returned. If n& is zero, LEFT$ returns an empty string. If the length value parameter is negative, it is interpreted as LEN(string_expression)-ABS(n&). For example, LEFT$("1234567890",-2) returns "12345678".

See also

EXTRACT$, INSTR, LTRIM$, MID$, RIGHT$, RTRIM$, TALLY, TRIM$, VERIFY

Example

' Demonstrate LEFT$ and RIGHT$ functions

DIM TestString$, x$, y$, n AS LONG

TestString$ = "ABCDEFGHIJKLMNOP"

FOR n = 1 TO 14 STEP 2

x$ = LEFT$(TestString$,n)

y$ = RIGHT$(TestString$,n)

NEXT n


LEN function

Purpose

Return the logical length of a variable, User-Defined Type, or Union.

Syntax

y& = LEN(target)

Remarks

If target is a variable or a string expression, LEN returns a value from 0 to the current string length, representing the number of characters in target. If target is a fixed-length string, the length of the fixed buffer is returned. If target is an ASCIIZ string, the length of the data stored in the ASCIIZ string is returned, not the maximum size of the ASCIIZ string. Use SIZEOF to determine the maximum size of an ASCIIZ string.

When used with pointers, LEN returns a value of 4, since a is always stored as a DWORD. You can use LEN with the target of a pointer to return the size of target. If the target is a dynamic string, you will receive the length of the string, not the length of the handle.

target can also be any other variable type, including and User-Defined Types (defined with TYPE/END TYPE). In that case, PowerBASIC will return the number of bytes needed to store a variable of that type.

When measuring the size of a padded (aligned) UDT structure with the LEN (or SIZEOF) statement, the measured length includes any padding that was added to the structure. For example, the following UDT structure:

TYPE LengthTestType DWORD

a AS INTEGER

END TYPE

...

DIM abc AS LengthTestType

x& = LEN(abc)

Returns a length of 4 bytes in x&, since the UDT was padded with 2 additional bytes to enforce DWORD alignment. Note that the LEN of individual UDT members returns the true size of the member without regard to padding or alignment. In the previous example, LEN(abc.a) returns 2.

See also

SIZEOF

Example

DIM p AS BYTE POINTER

ByteLen = LEN(p) ' size of a pointer = 4 bytes

ByteLen = LEN(@p) ' size of byte (target) = 1 byte


LET statement

Purpose

Assign a value to a variable, or assign a Dispatch Interface reference to an object variable in preparation to use a COM interface.

Syntax

[LET] variable = expression

[LET] objvar = NEW {DISPATCH | InterfaceName} {ON} {ProgramID$}

[LET] objvar = {DISPATCH | InterfaceName} {ON} ProgramID$

[LET] objvar1 = objvar2

[LET] objvar = NOTHING

[LET] objvar = vrntvar

[LET] vrntvar = objvar

[LET] vrntvar = vrntvar

[LET] vrntvar = EMPTY

[LET] vrntvar = ERROR numr

[LET] vrntvar = expression [AS vartype]

[LET] vrntvar = array()

[LET] array() = vrntvar

[LET] vrntvar = BYREF variable

Remarks

The LET keyword is entirely optional.

The following text describes the operations of the enhanced forms of the LET statement:

[LET] objvar = NEW {DISPATCH | InterfaceName} {ON} {ClassName | ProgramID$}

Upon execution, a new object (instance) is created, and a reference to that object is assigned to the object variable objvar. If the new object cannot be created (for example, there may be a lack of available memory or the requested interface might not be supported), the object variable is set to NOTHING. The success or failure of the operation can be tested with the ISOBJECT(ObjVar) and/or ISNOTHING(ObjVar) functions.

objvar

objvar must be dimensioned as an object variable of that Interface name: DIM objvar AS interfacename for early-binding, or DIM objvar AS DISPATCH for late-binding. Early-binding requires a declaration of the Interface with an INTERFACE/END INTERFACE block.

ProgramID$

ProgramID$ is not case-sensitive and may be a variable, string literal or string equate. The string expression ProgramID$ is presumed to evaluate to a PROGID name for an external COM Server (COM Object).

Interfacename

Name of the interface from the early binding declaration. Methods and Properties are accessed with the OBJECT statement.

[LET] objvar = {DISPATCH | InterfaceName} {IN | ON} ProgramID$

objvar

objvar must be dimensioned as an object variable of that Interface name: DIM objvar AS interfacename for early-binding, or DIM objvar AS DISPATCH for late-binding. Early-binding requires a declaration of the Interface with an INTERFACE/END INTERFACE block.

ProgramID$

ProgramID$ is not case-sensitive and may be a string variable, string literal or string equate. The string expression ProgramID$ is presumed to evaluate to a PROGID name for an external COM Server (COM Object) which is currently running and initialized via COM Services. For example, "Word.Application". In this case, COM services will be utilized to open an Interface on the existing object, and a reference to that object is assigned to the object variable objvar.

Interfacename

Name of the interface from the early binding declaration. Methods and Properties are accessed with the OBJECT statement.

If the operation is not successful (for example, if the interface specified is not supported on ProgramID$), the object variable objvar is set to NOTHING. The success or failure of the operation can be tested with the ISOBJECT(ObjVar) and/or ISNOTHING(ObjVar) functions.

[LET] objvar1 = objvar2

If both Object Variables have been declared as the same object type (the same interface name), the source variable (objvar2) is copied to the destination variable (objvar1), and the reference count of the object is incremented. If the object variables are of different object types, a new interface (of the type implied by objvar1) is opened on objvar2, and a reference to it is assigned to objvar1. If the operation is unsuccessful, objvar1 is set to NOTHING.

[LET] objvar = NOTHING

Destroys an object variable, discontinuing its association with a specific object. This in turn releases all system and memory resources associated with the object when no more object variables refer to it.

[LET] objvar = vrntvar

Attempts to open an interface of the specified class for objvar on the object of vrntvar, and assigns a reference to objvar. It assumes that vrntvar contains a reference to an object of type %VT_UNKNOWN or %VT_DISPATCH.

If the desired interface cannot be opened, the object variable objvar is set to NOTHING. The success or failure of the operation can be tested with the ISOBJECT(ObjVar) and/or ISNOTHING(ObjVar) functions.

[LET] vrntvar = objvar

Used to assign an object reference from an object variable to a variant variable. It attempts to open an IDispatch interface, else an IUnknown interface on the object of objvar, and assigns that reference to vrntvar.

Variant variables cannot contain references to custom interfaces, only IDispatch or IUnknown.

If the assignment is successful, VARIANTVT(vrntvar) will return either %VT_UNKNOWN or %VT_DISPATCH. If it is unsuccessful, vrntvar is set to %VT_EMPTY.

[LET] vrntvar = BYREF variable

Used to allow a variant to contain a typed to a specific variable. Any changes to the variant will cause the variable to be changed, as it is the target of the pointer. The variable may be of any data type which is supported by variants and COM objects: Byte, Word, Dword, Integer, Long, Quad, Single, Double, Currency, Variant, and dynamic String. If you attempt to use an unsupported variable type (like Extended, Bit, ASCIIZ, etc.), PowerBASIC will generate an Error 482 (Data Type Mismatch). Further, you may not use a register variable (automatic or explicit), or an Error 491 (Invalid Register Variable) will be generated. Note that strings used with COM objects are expected to be in Unicode format, rather than ANSI. The ACODE$ and UCODE$ functions may be used to convert the strings as necessary.

Assignments to variants

When working with variant variables, all necessary internals are managed appropriately by PowerBASIC. When setting a Variant to EMPTY, it is considered to contain no value at all. It is also possible to assign a specific COM error number (usually a COM specific error, such as %E_NOINTERFACE, etc) using the ERROR numr syntax.

When assigning values to variants, PowerBASIC automatically analyzes the expression and chooses an appropriate or string data type for the internal representation of the Variant. However, you can specify a particular preferred format by adding the optional AS vartype clause. For example:

LET xyz = 21.34 AS LONG

In the above example, the Variant would actually contain the value 21, as the programmer forced it to Long-integer representation. vartype may be one of the following keywords: BYTE, WORD, DWORD, INTEGER, LONG, QUAD, SINGLE, DOUBLE, CURRENCY, STRING.

The fifth and sixth forms allow you to assign an entire array to a Variant, or convert it back to a PowerBASIC array. In the case of a dynamic string array, the appropriate ANSI/Unicode conversions are handled automatically. This array assignment is always subject to the type compatibility of each component. For example, you cannot assign an Extended-precision floating-point array to a Variant, nor can you assign an array with more than eight dimensions to a PowerBASIC array.

Restrictions

LET cannot be used to assign a string to a User-Defined Type (UDT) or UNION. Such an assignment must be performed with the CSET, LSET, RSET, or TYPE SET statements. UDTs and Unions can be directly assigned to string variables.

See also

CSET, CSET$, LSET, OBJECT, RSET, SET, TYPE SET

Example

LET DummyStr$ = "This is a test."

LET TempStr$ = DummyStr$


LINE INPUT statement

Purpose

Read an entire line from the keyboard into a variable, ignoring delimiters.

Syntax

LINE INPUT ["prompt" | $string_equate] [,] string_variable

Remarks

prompt is an optional string constant or a string equate. LINE INPUT displays prompt on the screen and waits for user input. Keystrokes are accepted until you press ENTER, at which time the resulting string is loaded into string_variable.

string_variable may be a fixed-length, ASCIIZ, or dynamic string. For fixed-length and ASCIIZ strings, keyboard input that is longer than the string is truncated to fit into the string. Dynamic strings receive the complete keyboard input without truncation. string_variable may not be a UDT variable, although fixed-length and ASCIIZ UDT member variables are supported.

Restrictions

This statement always returns keyboard input; it is not subject to input redirection. LINE INPUT can now accept up to 9999 characters. LINE INPUT previously accepted only up to 128 characters.

See also

INKEY$, INSTAT, LINE INPUT#, WAITKEY$

Example

LINE INPUT "Please enter your name: " a$

PRINT "Hello "; a$


LINE INPUT# statement

Purpose

Read line(s) from a sequential file into a variable or string array, ignoring delimiters.

Syntax

LINE INPUT #filenum&, string_variable

LINE INPUT #filenum&, Arr$() [RECORDS rcds] [TO count]

Remarks

filenum& is the file number, or variable containing a file number, given when the file was opened. string_variable is the string variable to be loaded with the data read from the file.

string_variable may be a fixed-length, ASCIIZ, or dynamic string. For fixed-length and ASCIIZ strings, data that is longer than the string is truncated to fit into the string. Dynamic strings receive the data without truncation. string_variable may not be a UDT variable, although fixed-length and ASCIIZ UDT member variables are supported.

LINE INPUT# is intended for use with text files composed of lines terminated by CR/LF ($CRLF or CHR$(13,10)) sequences. It reads a line from the file and returns it, minus the CR/LF delimiter. Commas, quotation marks and other characters have no special meaning for LINE INPUT#, and are treated like any other text.

If the file consists of comma-delimited data items, INPUT# is likely to be more suitable then LINE INPUT#.

The second syntax definition of LINE INPUT# reads a file opened for INPUT, assigning full lines of text to each element of the array.

It is assumed the data is standard text, delimited by a CR/LF ($CRLF) or EOF (1A hex or $EOF). LINE INPUT# attempts to read the number of lines specified in the RECORDS rcds option, or the number of elements in the array, whichever is smaller.

The actual number of lines read is assigned to the variable specified in the optional TO count clause. FILESCAN is useful in conjunction, to determine the dimensioned size of the string array. EOF is set just as with single Line Input.

See also

EOF, FILESCAN, INPUT#, PRINT#

Example

SUB MakeFile

' Open a sequential file for output. Use PRINT#

' to write different data types to the file.

OPEN "LINEINP#.DTA" FOR OUTPUT AS #1

' Define some variables.

sVar$ = "There's trouble in River City, by George."

iVar% = 1000

fpVar! = 30000.12

' Write a line of text to the file.

PRINT# 1, sVar$; iVar%; fpVar!

CLOSE #1 'close the file

END SUB 'end procedure MakeFile

SUB ReadFile

'Open a sequential file for input, then use

'LINE INPUT # to read lines of different

'data types from the file.

OPEN "LINEINP#.DTA" FOR INPUT AS #1

StringVar$ = ""

'Input an entire line regardless of length or

'delimiters.

LINE INPUT #1, StringVar$

CLOSE #1 'close the file

END SUB 'end procedure ReadFile


LO function

Purpose

Extract the least significant (low-order) portion of an value.

Syntax

result = LO(type, value)

Remarks

The value returned by LO is unsigned if type is BYTE, WORD, or DWORD, and signed if type is INTEGER or LONG. value may be up to twice the size of the data type specified by type. In the following example, n may be up to a 16-bit value (twice the size of a BYTE):

b = LO(BYTE,n)

Restrictions

LO replaces LOBYT, LOWRD, and LOINT. Note that those functions may not be supported in future versions of PowerBASIC, so update your code to use the new syntax.

See also

HI, MAK


LOBYT function

Purpose

Extract the least significant (low-order) byte from an Integer or Word value, and return it as an unsigned Byte value.

LOBYT has been superceded by the LO function, although LOBYT remains supported for a limited period. Existing code should be converted to the new syntax as soon as possible.

Syntax

bResult? = LOBYT(sixteenbitvalue)

Remarks

The value returned by LOBYT is always unsigned, regardless of the sign of the argument. Effectively, this function provides the same result as PEEKing at the first byte of the argument.

See also

HI, HIBYT, HIINT, HIWRD, LO, LOINT, LOWRD, MAKDWD, MAKINT, MAKLNG, MAKPTR, MAKWRD


LOC function

Purpose | Determine the current seek position in an open disk file.
Syntax | qResult&& = LOC([#] filenum&)
Remarks | LOC is provided for compatibility with older BASICs. It is recommended that code is modified to use the SEEK function instead. The Number symbol (#) is optional, but recommended for clarity.
See also | FILEATTR, SEEK function, SEEK statement


LOCAL statement

Purpose

Declare local variables inside a Sub or Function. Local variables retain their values only until the end of the Sub or Function.

Syntax

LOCAL variable[()] [AS type] [, variable[()]] [...]

LOCAL variable[()] [, variable[()]] [, ...] AS type

Remarks

The LOCAL statement is valid only inside a Sub or Function. Local variables lose their values when the Sub or Function ends. Storage space for local variables is allocated on the stack, and each local variable is initialized to zero (or, for variables, an empty string) each time the enclosing Sub or Function is called.

To declare an array as a local variable, use an empty set of parentheses in the variable list: You can then use the DIM statement to dimension the array.

LOCAL MyArray%()

LOCAL StringArray() AS STRING

The LOCAL statement may, optionally, accept a list of variables, all of which are defined by the type descriptor keyword that follows them. For example:

LOCAL aaa, bbb, ccc AS INTEGER

LOCAL vptr, aptr() AS LONG PTR

Restrictions

DEFtype has no effect on variables defined by a LOCAL statement.

See also

DIM, GLOBAL, STATIC, THREADED

Example

Test% = 100

ShowText "Before: " + STR$(Test%)

CALL Locals

ShowText "After: " + STR$(Test%)

SUB Locals

LOCAL Test%

Test% = 0

ShowText "In SUB: " + STR$(Test%)

END SUB

Result

Before: 100

In SUB: 0

After: 100


LOCATE statement

Purpose

Set the caret position.

Syntax

LOCATE [row%] [, [column%]]

Remarks

row% specifies the screen row (starting at 1) at which to position the caret, in the range 1 through SCREENY.

column% specifies the screen column (starting at 1) at which to position the caret, in the range 1 through SCREENX.

If either expression is missing or invalid, that particular coordinate is left unchanged.

For example, LOCATE , 10 moves the caret to column 10 on the current line.

The caret visibility and size can be adjusted with the CURSOR statement, and the size can be queried with the CURSOR function.

See also

CURSOR function, CURSOR statement, CURSORX, CURSORY, SCREENX, SCREENY

Example

CLS

LOCATE 13, 34

PRINT "Hello from PB/CC!"

WAITKEY$


LOCK statement

Purpose

Lock part or all of an open file, to prevent other processes from accessing it.

Syntax

LOCK [#] filenum& [, {record&& | start&& TO end&&}]

Remarks

LOCK prevents another process from accessing a record, range of records, byte, or range of bytes in a file opened as file number filenum&.

If the file was opened in random-access mode, record&&, start&&, and end&& specify record numbers. When used with binary mode files, record&&, start&&, and end&& specify byte positions, starting from either zero or one (the default).

If a record is specified, only that record (or byte) is locked. Otherwise, a range of records (or bytes) is locked, from start&& to end&&.

If no records are specified, or if the file was opened in sequential mode, the entire file is locked.

All records (or bytes) to be locked must be subsequently unlocked using the UNLOCK statement. Multiple locks may be placed on a file, and locks may be unlocked in any order. However, the parameters used for each UNLOCK statement must exactly match those used for the previous corresponding LOCK statement.

All locked records (or bytes) must be unlocked using the UNLOCK statement before the file can be closed.

If a lock attempt fails, PowerBASIC sets the ERR system variable to reflect a run-time Error 70 ("Permission denied"), or Error 75 ("Path/file access error").

See also

OPEN, UNLOCK

Example

OPEN "PATIENTS.DAT" FOR RANDOM AS #1 LEN = 1024

... ' determine the record number to retrieve

LOCK #1, recnum

GET #1, recnum

... ' process the record here

PUT #1, recnum

UNLOCK #1, recnum

CLOSE #1


LOF function

Purpose | Return the length of an open disk file.
Syntax | y&& = LOF([#] filenum&)
Remarks | filenum& is the file number with which the file was opened. LOF returns the size of the indicated file in bytes, in the Quad-integer range 0 to 2^63-1. The Number symbol (#) is optional, but recommended for clarity.
See also | FILEATTR, LOC, SEEK function, SEEK statement
Example | OPEN "RECIPES.DAT" FOR BINARY AS #1 x&& = LOF(1) CLOSE #1


LOG, LOG2 and LOG10 functions

Purpose

LOG returns the natural (base e) logarithm of its argument. LOG2 returns the base 2 logarithm. LOG10 returns the common (base 10) logarithm.

Syntax

y = LOG(numeric_expression)

y = LOG2(numeric_expression)

y = LOG10(numeric_expression)

Remarks

A logarithm of a number is the power to which the base would have to be raised to yield the number. Thus:

logarithm (base b) of n = x if b^x = n

and:

(base)^log(n) = n

The EXP functions complement the LOG functions. For example, if s = LOG(t), then t = EXP(s).

By definition, the logarithm (any base) of 1 is 0. LOG returns the natural logarithm (base e, where e = 2.718282...) of its argument. LOG2 and LOG10 return the logarithm for base 2 and 10, respectively.

numeric_expression must be a value of zero or greater.

LOG, LOG2, and LOG10 return Extended-precision values.

See also

EXP, EXP2, EXP10, SQR, Arithmetic Operators


LOINT function

Purpose

Extract the least significant (low-order) Word from a Long-integer or Double-word (DWORD) value, and return it as a signed Integer value.

LOINT has been superceded by the LO function, although LOINT remains supported for a limited period. Existing code should be converted to the new syntax as soon as possible.

Syntax

iResult% = LOINT(thirtytwobitvalue)

Remarks

The value returned by LOINT is always signed, regardless of the sign of the argument.

See also

LO


LOWRD function

Purpose

Extract the least significant (low-order) Word from a Long-integer or Double-word (DWORD) value, and return it as an unsigned Word value.

LOWRD has been superceded by the LO function, although LOWRD remains supported for a limited period. Existing code should be converted to the new syntax as soon as possible.

Syntax

wResult?? = LOWRD(thirtytwobitvalue)

Remarks

The value returned by LOWRD is always unsigned, regardless of the sign of the argument.

See also

LO


LPRINT ATTACH statement

Purpose

Connect to a line-printer device for use with LPRINT.

Syntax

LPRINT ATTACH device$

Remarks

LPRINT ATTACH attempts a direct connection to the specified [line] printer device. A line printer is one that will accept standard ASCII text and any device-specific control codes, such as CR, LF, and FF.

A line printer is named by the port to which it is attached (LPT1, etc.) because the data is sent directly to the port, not through a device driver. That is, LPRINT communicates directly with the attached line printer device, bypassing the spooler and printer driver. Therefore, any settings such as "work offline" in the Printer Properties dialog will be ignored.

Once the printer is attached by LPRINT ATTACH, print data can be sent to it with the LPRINT statement.

LPRINT ATTACH allows you to change the printer device used by LPRINT operation. When executed, the current connection (if any) is closed and the new connection is established. No colon is used in the device name. For example, to connect to LPT2:

LPRINT ATTACH "LPT2"

or to a printer on a network server:

LPRINT ATTACH "\\SERVER\HPLJ5"

device$ must be a valid device name and cannot exceed 32 characters in length. In some circumstances, such as with the Novell network client, LPRINT ATTACH with a UNC name may be rejected, and the LPRINT ATTACH will be unsuccessful, and a subsequent LPRINT$ test will return an empty string.

If LPRINT ATTACH is not successful, an Error 68 ("Device unavailable") is generated and LPRINT$ returns a nul (empty) . If no LPRINT ATTACH is ever executed (successful or not), PowerBASIC will attempt to connect to the line printer at LPT1. Once any LPRINT ATTACH is attempted, no default to LPT1 will be presumed.

Care must be used with line printers in Windows, since if there is no available printer attached to the port, program execution may be suspended, with no errors. So, it is wise to use LPRINT ATTACH to explicitly connect the intended printer device, and test for the successful connection by the examination of LPRINT$ and ERR. For example:

ERRCLEAR

LPRINT ATTACH "LPT3"

IF ERR OR LPRINT$ = "" THEN PRINT "Connection failed"

Once all the data has been sent to the printer, detach the printer so other applications can use it., with the LPRINT CLOSE statement

Note: The Win32 API call EnumPrinters can give you a list of all valid printers and print devices, or you can enumerate the list of printers with the PRINTERCOUNT and PRINTER$ functions.

Restrictions

If device$ is an empty string, the current connection (if any) is detached. This is equivalent to the LPRINT CLOSE statement.

See also

LPRINT, LPRINT CLOSE, LPRINT FLUSH, LPRINT FORMFEED, LPRINT$, XPRINT, XPRINT ATTACH

Example

' Typical LPRINT printing strategy

ERRCLEAR

LPRINT ATTACH "LPT2" ' Use LPT2 device

IF (ERR<>0) OR (LEN(LPRINT$)) THEN

LPRINT "This is your line-printer talking"

LPRINT FORMFEED ' Issue a formfeed

LPRINT FLUSH ' flush the buffer

LPRINT CLOSE ' detach the printer

END IF


LPRINT CLOSE statement

Purpose | Disconnect the current printer device.
Syntax | LPRINT CLOSE
Remarks | LPRINT CLOSE detaches the currently selected printer connection (established with the LPRINT ATTACH statement) from LPRINT operations, allowing the spooler subsystem to commence print operations. Once a connection is closed, LPRINT$ will return an empty printer device name string until a new connection is established. LPRINT CLOSE is equivalent to using LPRINT ATTACH with an empty printer device name string.
Restrictions | LPRINT CLOSE is an essential step in the print process. To ensure the printer device is available to other applications, printers should always be closed when not in use. Failing to close a connection may cause significant delays before printing commences. In some cases, some or all of the print data may be lost.
See also | LPRINT, LPRINT ATTACH, LPRINT FLUSH, LPRINT FORMFEED, LPRINT$, XPRINT, XPRINT ATTACH
Example | ' Typical LPRINT printing strategy ERRCLEAR LPRINT ATTACH "LPT2" ' Use LPT2 device IF ISTRUE ERR OR ISFALSE LEN(LPRINT$) THEN LPRINT "This is your line-printer talking" LPRINT FORMFEED ' Issue a formfeed LPRINT FLUSH ' flush the buffer LPRINT CLOSE ' detach the printer END IF


LPRINT FLUSH statement

Purpose

Flush any remaining print data to a printer device and signal the start of the print process.

Syntax

LPRINT FLUSH

Remarks

LPRINT FLUSH forces the operating system to flush any buffered data and begin printing. Use LPRINT FLUSH to ensure print data is submitted to the printer as soon as possible, rather than waiting for any timeout period to elapse first. Depending upon the printer and its drivers, printing may begin immediately, or it may be delayed until execution of an LPRINT CLOSE statement.

Typically, an LPRINT FLUSH statement is preceded with a FORMFEED statement, so ensure that the print job is ejected normally from the printer device.

See also

LPRINT, LPRINT ATTACH, LPRINT CLOSE, LPRINT FORMFEED, LPRINT$, XPRINT, XPRINT ATTACH

Example

' Typical LPRINT printing strategy

ERRCLEAR

LPRINT ATTACH "LPT2" ' Use LPT2 device

IF ISTRUE ERR OR ISFALSE LEN(LPRINT$) THEN

LPRINT "This is your line-printer talking"

LPRINT FORMFEED ' Issue a formfeed

LPRINT FLUSH ' flush the buffer

LPRINT CLOSE ' detach the printer

END IF


LPRINT FORMFEED statement

Purpose | Send a formfeed (page eject) character to an attached printer device.
Syntax | LPRINT FORMFEED
Remarks | For direct connections, LPRINT FORMFEED sends a form-feed character (ASCII character 12, $FF, or CHR$(12)) to the attached line printer device, to ensure the current page will be ejected. For host-based connections, PowerBASIC signals to the printing subsystem to perform the page eject operation. Typically, an LPRINT FORMFEED is performed before a LPRINT FLUSH and LPRINT CLOSE.
See also | LPRINT, LPRINT ATTACH, LPRINT CLOSE, LPRINT FLUSH, LPRINT$, XPRINT, XPRINT ATTACH
Example | ' Typical LPRINT printing strategy ERRCLEAR LPRINT ATTACH "LPT2" ' Use LPT2 device IF ISTRUE ERR OR ISFALSE LEN(LPRINT$) THEN LPRINT "This is your line-printer talking" LPRINT FORMFEED ' Issue a formfeed LPRINT FLUSH ' flush the buffer LPRINT CLOSE ' detach the printer END IF


LPRINT$ function

Purpose

Return the name of the printer device used for operations.

Syntax

device$ = LPRINT$

Remarks

LPRINT$ returns the name of the currently attached printer device used by the LPRINT statement. If there is no attached device, an empty is returned.

LPRINT$ is primarily used to detect if an LPRINT ATTACH operation was successful.

See also

LPRINT, LPRINT ATTACH, LPRINT CLOSE, LPRINT FLUSH, LPRINT FORMFEED, XPRINT, XPRINT ATTACH

Example

ERRCLEAR

LPRINT ATTACH "LPT3"

IF ERR <> 0 OR LPRINT$ = "" THEN PRINT "Printer connection failed"


LPRINT statement

Purpose

Output (device-dependent) text and data to a printer device.

Syntax

LPRINT [expression] [SPC(n)] [TAB(n)] [,] [;]

Remarks

The LPRINT functionality is identical to the traditional PRINT statement, except that the data is sent directly to a line printer rather than to a display. A line printer is one which will accept standard ASCII text and associated control codes, such as $CR, $LF, and $FF.

PowerBASIC inserts a carriage return and linefeed at the end of each printed line. A semi-colon between expressions is an optional delimiter which leaves the printer column position unchanged. A comma moves the printer position to the next column of 14 positions each. A trailing semi-colon suppresses the final CR/LF. If TAB(n) is less than the current printer position, output is placed at the requested position on the following line.

Before you execute an LPRINT statement, you must explicitly connect to the intended line printer using the LPRINT ATTACH statement. If the connection to the device is unsuccessful, all LPRINT statements are ignored until a valid printer device has been attached. LPRINT communicates directly with the attached device, bypassing the Windows operating system and printer driver. Therefore, any settings such as "work offline" in your printer properties dialog will be ignored.

Once all the data has been sent to the printer, detach the printer so other applications can use it., with the LPRINT CLOSE statement

Host-based (Windows-only) printers use proprietary control protocols so, sending print data to them with LPRINT is unlikely to produce any output at all. PowerBASIC supports host-based printers through and related statements.

See also

LPRINT ATTACH, LPRINT CLOSE, LPRINT FLUSH, LPRINT FORMFEED, LPRINT$, XPRINT, XPRINT ATTACH

Example

' Typical LPRINT printing strategy

ERRCLEAR

LPRINT ATTACH "LPT2" ' Use LPT2 device

IF ISFALSE ERR AND ISTRUE LEN(LPRINT$) THEN

LPRINT "This is your line-printer talking"

LPRINT FORMFEED ' Issue a formfeed

LPRINT FLUSH ' flush the buffer

LPRINT CLOSE ' detach the printer

END IF


LSET$ function

Purpose

Return a containing a left-justified (padded) string.

Syntax

a$ = LSET$(string_expression, strlen& [USING ustring_expression])

Remarks

LSET$ left-aligns the string string_expression into a string of strlen& characters.

USING

If ustring_expression is null (empty) or is not specified, LSET$ pads string_expression with space characters. Otherwise, LSET$ pads the string with the first character of ustring_expression.

If string_expression is shorter then strlen&, LSET$ left-justifies string_expression within result_var, padding the right side as described above; otherwise, LSET$ returns the left-most strlen& bytes of string_expression.

See also

CSET, CSET$, GET, LET, LSET, PUT, RESET, RSET, RSET$, STRINSERT$, TYPE SET

Example

a$ = LSET$("PowerBASIC", 20)

' result: "PowerBASIC "

a$ = LSET$("PowerBASIC",20 USING "*")

' result: "PowerBASIC**********"


LSET statement

Purpose

Left-align a within the space of another string or User-Defined Type.

Syntax

LSET [ABS] result_var = string_expression [USING ustring_expression]

Remarks

LSET left-aligns a string into the space of another string or variable of a User-Defined Type.

ABS

If ABS is specified, or ustring_expression is null (empty), LSET leaves the padding positions unchanged from their original content, rather than replacing them with spaces.

USING

If string_expression is shorter then result_var, LSET left-justifies string_expression within result_var, and pads remaining character positions on the right side using the first character in ustring_expression or spaces if not specified or is null (empty).

If string_expression is longer than result_var, LSET truncates string_expression from the right until it fits in result_var.

LSET can be used to assign the content of a User-Defined Type to a User-Defined Type variable of a different class, or assign a dynamic string to a User-Defined Type. For example:

LSET MyType = STRING$(LEN(MyType), 0)

LSET MyType = a$

RSET works similarly, but performs right-justification; CSET performs center-justification.

See also

CSET, CSET$, GET, LET, LSET$, PUT, RESET, RSET, RSET$, STRINSERT$, TYPE SET

Example

a$ = "SuperBASIC=SuperBASIC"

LSET ABS a$ = "PowerBASIC"

' result: "PowerBASIC=SuperBASIC"

LSET a$ = "PowerBASIC" USING "*"

' result: "PowerBASIC***********"


LTRIM$ function

Purpose

Return a copy of a , with leading characters or strings removed.

Syntax

x$ = LTRIM$(MainString [, [ANY] MatchString])

Remarks

MainString is the string expression from which to remove characters, and MatchString is the string expression containing the characters to remove.

If MatchString is not specified, LTRIM$ removes leading spaces. LTRIM$ returns a sub-string of MainString, from the first non-MatchString (or non-space) to the end of the string. If MatchString (or a space) is not present at the beginning of MainString, all of MainString is returned.

If the ANY keyword is included, MatchString specifies a list of single characters to be searched for individually. A match on any one of these as a leading character will cause the character to be removed from the result.

LTRIM$ is case sensitive.

See also

EXTRACT$, INSTR, LEFT$, MID$, REMOVE$, REPLACE, RIGHT$, RTRIM$, STRDELETE$, STRINSERT$, STRREVERSE$, TALLY, TRIM$, VERIFY

Example

A$ = "0123ABC3210"

A$ = LTRIM$(A$, ANY "0123456789")

Result

ABC3210


MACRO/END MACRO block

Purpose

Define a single or multi-line text substitution block.

Syntax

Single line macro:

MACRO macroname [(prm1, prm2, ...)] = replacementtext

Multi-line macro:

MACRO macroname [(prm1, prm2, ...)]

[MACROTEMP ident1 [, ident2, ...]]

DIM ident1 AS type [, ident2 AS type, ...]]

{replacementtext}

[EXIT MACRO]

{replacementtext}

END MACRO

Macro function:

MACRO FUNCTION macroname [(prm1, prm2, ...)]

[MACROTEMP ident1 [, ident2, ...]

DIM ident1 AS type [, ident2 AS type, ...]]

{replacementtext}

[EXIT MACRO]

{replacementtext}

END MACRO = returnexpression

Remarks

Macro is a powerful text substitution construct that may take a single-line or multi-line format. It generates absolutely no executable code unless it is referenced, and effectively allows the programmer to design a part of the PowerBASIC language to his/her own needs and requirements. For example, a simple single-line macro can allow PowerBASIC to emulate the CONST syntax used in Visual Basic - see the box-out below for more information.

A macro must always be defined before it is referenced, and the parameter count must always match the definition. When a macro is referenced, the occurrence of the name is replaced by the defined replacement text, expanded with parameter substitution. The first line of a MACRO definition is termed the macro prototype, and this line may not be split into multiple logical lines with the underscore (_) line continuation character. Likewise, the END MACRO = returnexpression may not be split with underscores either.

Macros may be nested, and may forward-reference other macros. However, care should be exercised to avoid circular references.

A single-line macro or a macro function may be referenced at any source code position which, when expanded, will be syntactically correct (also see the Restrictions section below). Consider the following simplistic example:

MACRO concatenate(prm1,prm2) = prm1 & $SPC & prm2

...

A$ = concatenate("Hello","World")

During compilation, PowerBASIC would internally expand this code to become:

A$ = "Hello" & $SPC & "World"

A multi-line macro, while more powerful in terms of coding, may be referenced only in the "statement" position, which is the first position on a line. That single reference is internally expanded into multiple lines of inline code to perform a complex task. For example:

MACRO Display6times(prm1)

CALL Display(prm1) : CALL Display(prm1)

CALL Display(prm1) : CALL Display(prm1)

CALL Display(prm1) : CALL Display(prm1)

END MACRO

...

Display6times("This is very cool...")

The single-line MACRO offers a cunning way to retain the CONST syntax used in MSBASIC and Visual Basic in your PowerBASIC code, while maintaining the low overhead advantage of PowerBASIC. For example:

MACRO CONST = MACRO

...

CONST Version = 1&

CONST AppTitle = "My Application"

...

a$ = AppTitle & " v" & FORMAT$(Version)

During compilation, the CONST keyword is replaced by the MACRO keyword, dynamically creating a new macro that, in turn, defines a numeric or string literal. When the real macro name is referenced in the code, the literal is substituted directly.

MACROTEMP

The MACROTEMP statement may be used to specify a list of one or more identifiers, each of which is automatically made unique to each expansion of a multi-line macro. This is done by internally appending the digits 0001, 0002, etc, to the identifier upon each expansion of the macro.

A text identifier may represent a variable, label, or any other word, which expands appropriately to avoid a duplicate name conflict in your code.

MACROTEMP just creates a symbol name. If this symbol is a variable name, the variable must still be formally declared with an appropriate DIM (or LOCAL) statement. For example:

MACRO CopyUntilNul(ptr1,ptr2)

MACROTEMP LoopPoint, ByteVar

DIM ByteVar AS BYTE

LoopPoint:

ByteVar = @ptr1

@ptr2 = ByteVar

INCR ptr1

INCR ptr2

IF ByteVar <> 0 THEN LoopPoint

END MACRO

Using that MACRO definition, the code "CopyUntilNul(Source, Dest)" would expand to something like this:

DIM ByteVar0001 AS BYTE

LoopPoint0001:

ByteVar0001 = @Source

@Dest = ByteVar0001

INCR Source

INCR Dest

IF ByteVar0001 <> 0 THEN LoopPoint0001

If the MACROTEMP statement were not used, serious naming conflicts would occur most any time that a macro was expanded more than once in a program. MACROTEMP statements may appear 0, 1, or more times in a macro definition, but they must always precede any other text in the macro.

MACROTEMP statements should be used with any label in a macro that may be expanded more than once in a program, and with any variable that should not be shared with any other expansion of the macro.

EXIT MACRO

EXIT MACRO may be used to terminate execution of code in the current macro expansion. It is functionally identical to the imaginary concept of GOTO END-MACRO.

END MACRO

A macro function block can return a value with the END MACRO = returnexpression statement.

Restrictions

A macro definition may contain replacement text up to approximately 4000 characters. Macros may specify up to 240 parameters, which may occupy up to approximately 2000 bytes total expanded space per macro.

Macro Function substitutions are limited to an expanded total of approximately 16000 characters per line of original source code.

Macro parameters are substituted directly, so whitespace characters in the passed macro parameters may cause unexpected problems if the expanded code is syntactically incorrect with the additional whitespace. For example, this can be important when specifying UDT variables as macro parameters. Consider the following code:

TYPE MyType

lCount AS LONG

szText AS ASCIIZ * 256

END TYPE

MACRO PresetUDT(u)

u.lCount = 1

u.szText = SPACE$(256)

END MACRO

FUNCTION PBMAIN

DIM x AS MyType

PresetUDT(x)

PresetUDT(x ) ' This line causes an Error 526

END FUNCTION

In the code above, the second macro expansion fails to compile because the trailing space in the passed macro parameter becomes part of the expanded code. In this situation, this additional space character breaks the syntax of the UDT variable reference within the expanded macro, triggering a compile-time Error 526 ("Period not allowed"). If we examine how the two expanded macro statements would appear, the problem becomes immediately obvious:

x .lCount = 1

^

x .szText = SPACE$(256)

^

(Please note that the caret symbols (^) above have been added purely to illustrate the exact position of the problem)

When using single-line macros that contain numeric expressions, use parentheses around the macro body to guard against unexpected order of precedence problems when the macro is used within an expression. For example, consider the following macro and expansion:

MACRO Calculate(p1, p2, p3) = (p1 * p2) \ p3

...

x = Calculate(a,b,c) ^ 3

When this macro is expanded, the expression would be calculated as follows:

x = (a * b) \ c ^ 3

However, if the macro body was enclosed in parentheses:

MACRO Calculate(p1, p2, p3) = ((p1 * p2) \ p3)

…then the expanded expression would be calculated thus:

x = ((a * b) \ c) ^ 3

MACRO prototypes (those beginning with the MACRO keyword) and END MACRO = returnexpression lines must be constructed on a single line of source code. That is, they may not be split across multiple lines of source code with line continuation characters, since these interfere with the text substitution process. For example, the following prototype is invalid:

MACRO FUNCTION MyMacro1(sParam1, sParam2, sParam3, sParam4)

If a macro expands directly to a Function call, the macro can be called using the SUB-style syntax, automatically discarding the function return value. For example:

MACRO sm(Msg) = SendMessage(a, Msg, b, c)

…can be called like this (if the return value is not required):

sm(x)

A macro cannot expand directly to a REMark, because REM and ' are processed before the macro is assigned. So, MACRO hello = REM winds up as an invalid, blank macro.

Finally, it should be noted that the Integrated Debugger appears to step over macro references as if they were conventional BASIC statements. This occurs because macro expansion takes place during the compilation process and the original source code is not affected or altered by the compile-time expansion.

See also

EXIT, FUNCTION/END FUNCTION, SUB/END SUB

Example

' Single-line macro:

MACRO muldivide(p1, p2, p3) = ((p1 * p2) / p3)

...

x = muldivide(3,3,2) + 10

' Multi-line macro and macro function example:

MACRO FUNCTION HowDidIGetHere

MACROTEMP i, a

DIM i AS LONG, a$

FOR ix = CALLSTKCOUNT TO 1 STEP -1

A$ = A$ + CALLSTK$(ix) + ", "

NEXT

END MACRO = RTRIM$(A$, ANY ", ")

MACRO DisplayText(txt)

#IF %DEF(%PB_CC32)

PRINT txt

#ELSE

MSGBOX txt

#ENDIF

END MACRO

SUB Testing2(r AS LONG,z AS ASCIIZ)

DisplayText(HowDidIGetHere)

END SUB

SUB testing1(z AS ASCIIZ)

DisplayText(HowDidIGetHere)

CALL Testing2(1,z)

END SUB

FUNCTION PBMAIN

DisplayText(HowDidIGetHere)

CALL Testing1("This is a test")

END FUNCTION

' Useful Macro functions

MACRO Pi = 3.141592653589793##

MACRO DegreesToRadians(dpDegrees) = (dpDegrees * 0.0174532925199433##)

MACRO RadiansToDegrees(dpRadians) = (dpRadians * 57.29577951308232##)

MACRO Hex64(x) = HEX$(HI(DWORD,x),8) + HEX$(LO(DWORD,x),8)


MAK function

Purpose

Create an value of a specified data type.

Syntax

resultvar = MAK(datatype, loworderval, highorderval)

Remarks

Create an integer class value of a specified data type (WORD, DWORD, , INTEGER, LONG, QUAD) from a low-order and a high-order part.

The complements to this function are the HI, HIWRD, HIINT, LO, LOINT and LOWRD functions, which may be used to split a single 32-bit value into two 16-bit components.

Restrictions

MAK supercedes the MAKWRD, MAKDWD, and MAKPTR functions. Those functions may not be supported in future versions of PowerBASIC, so update your code to use the new syntax.

See also

HI, LO

Example

dwResult = MAK(DWORD, x??, y??)


MAKDWD, MAKLNG and MAKPTR functions

Purpose

Take the low-order 16-bits from each of two variables and combine to produce a single 32-bit value.

MAKDWD, MAKLNG and MAKPTR have been superceded by the MAK function, although they remain supported for a limited period. Existing code should be converted to the new syntax as soon as possible.

Syntax

value??? = MAKDWD(loword, hiword)

value& = MAKLNG(loword, hiword)

value??? = MAKPTR(loword, hiword)

Remarks

MAKDWD, MAKLNG and MAKPTR take the low-order 16-bits from each of loword and hiword, and combine them into a single 32-bit value. MAKDWD returns an unsigned 32-bit DWORD value, MAKLNG returns a signed 32-bit Long-integer value and MAKPTR returns an unsigned 32-bit DWORD (pointer) value.

loword

Forms the least significant or "low-order" 16-bits of the 32-bit result value.

hiword

Forms the most significant or "high-order" 16-bits of an 32-bit result value.

See also

MAK


MAKINT and MAKWRD functions

Purpose

Take the low-order byte from each of two variables and combine to produce a single 16-bit value.

MAKINT and MAKWRD have been superceded by the MAK function, although they remain supported for a limited period. Existing code should be converted to the new syntax as soon as possible.

Syntax

value% = MAKINT(lobyte, hibyte)

value?? = MAKWRD(lobyte, hibyte)

Remarks

MAKINT and MAKWRD take the low-order byte from each of lobyte and hibyte, and combine them into a single 16-bit value. MAKINT returns a signed 16-bit integer value, and MAKWRD returns an unsigned 16-bit WORD value.

lobyte

Forms the least significant or "low-order" byte of the 16-bit result value.

hibyte

Forms the most significant or "high-order" byte of the 16-bit result value.

See also

MAK


MAT statement

Purpose

To simplify Matrix Algebra calculations.

Syntax

MAT a1() = CON 'Set all elements of a1() to one

MAT a1() = CON(expr) 'Set all elements of a1() to value of expr

MAT a1() = IDN 'Establish a1() as an identity matrix

MAT a1() = ZER 'Set all elements of a1() to zero

MAT a1() = a2() + a3() 'Addition

MAT a1() = a2() 'Assignment

MAT a1() = INV(a2()) 'Inversion

MAT a1() = (expr) * a2() 'Scalar Multiplication

MAT a1() = a2() - a3() 'Subtraction

MAT a1() = a2() * a3() 'Multiplication

MAT a1() = TRN(a2()) 'Transposition

Remarks

Array names with the MAT statements may optionally include a set of empty parentheses. The following are both equally valid, but the inclusion of the parentheses improves clarity of the code:

MAT a1 = CON

MAT a1() = CON

MAT CON, IDN ZER + - = and TRN operations are valid with Byte, Word, Double-word, Integer, Long-integer, Quad-integer, Single-precision, Double-precision and Extended-precision arrays.

Matrix * and INV operations are only valid with floating-point numbers: Single-precision, Double-precision and Extended-precision arrays.

It is the programmer's responsibility to ensure that arrays used with MAT are of the appropriate size and type. All operations involving two or more arrays require that they be of exactly the same size and type, without exception. Failure to adhere causes undefined results. In the interest of execution speed, no error checking is performed at run-time.

Every scalar value denoted here as 'expr' must be enclosed in parentheses. Although Matrix operations tend to imply a two-dimensional array, unless otherwise noted (such as with MAT IDN, *, TRN), MAT may be used with arrays of one to eight dimensions. It is permissible to specify one array for multiple MAT parameters.

Example

MAT array1() = IDN

This establishes array1 as an identity matrix, with all diagonal elements as 1 and all others as zero. This produces undefined results if array1 is not a "square" matrix.
MAT array1() = (expr) * array2()

Each element of array2 is multiplied by the scalar value of the expr, then assigned to array1.

MAT array1() = TRN(array2())

Transposes the row and columns from array2 to array1. Arrays must be equivalent: array1(5,2) and array2(2,5). Only a square matrix may be transposed to itself.

MAT array1() = INV(array2())

Inverts the array from array2 to array1. Only a square matrix may be inverted. Proof: If array1 is then multiplied by array2, the resulting "array3" will be equal to an Identify Matrix, (MAT array3 = array1 * array2 ' array3 should now be equal to "MAT array3 IDN").

MAT a() = b() * c()

Array multiplication occurs as follows:

' Row Column assumption:

' array [a]l,n = [b]l,m * [c]m,n

FOR i = 1 TO l ' Row [a]l = Row [b]l

FOR j = 1 TO n ' Column [a]n = Column [c]n

a(i,j) = 0# ' # if Double-precision

FOR k = 1 TO m ' Column [b]m = Row [c]m

a(i,j) = a(i,j) + b(l,k) * c(k,j)

NEXT

NEXT

NEXT


MAX function

Purpose

Return the argument with the largest (maximum) value.

Syntax

y = MAX(arg [, arg] ...)

y& = MAX&(arg& [, arg&] ...)

y$ = MAX$(arg$ [, arg$] ...)

Remarks

These functions take any number of arguments and return the argument with the largest (maximum) value. MAX handles arguments of any type.

MAX& handles arguments which evaluate to Long-integers (MAX& is more efficient than MAX).

MAX$ handles arguments.

If any arguments of MAX& are outside of the range of Long-integers, the result is undefined. Any arguments of MAX& will be rounded to Long-integers before the comparison begins.

MAX% is recognized as a valid synonym for MAX&.

See also

CHOOSE, CHOOSE&, CHOOSE$, IIF, IIF&, IIF$, MIN, MIN&, MIN$, SWITCH, SWITCH&, SWITCH$

Example

x% = MAX&(A, B, C, D)

x$ = MAX$("abacadabra", "cad", A$, B$(4), C$+D$+LEFT$(E$,5))

x## = MAX(1.1@@, A%/B!, C#(x)^D, E##, SIN(F&))


MCASE$ function

Purpose

Return a mixed case version of its argument.

Syntax

s$ = MCASE$(string_expression [,ANSI | OEM])

Remarks

Forces the string_expression contents to mixed case. The first letter of each word is capitalized, while the remaining characters are converted to lowercase. A "word" is construed as a consecutive series of letters - any character that is not a letter is not considered part of a word.

The optional ANSI or OEM parameter specifies whether string_expression is converted to mixed case using the ANSI or OEM charset. If no charset option is selected, then the Console’s current OEM chareset is used to convert string_expression to mixed case. The PowerBASIC Compiler for Windows defaults to using the systems ANSI charset to convert string_expression. Only "International" characters in the range CHR$(128) to CHR$(255) are affected by the charset parameter.

See also

LCASE$, UCASE$

Example

x$ = MCASE$("Cats aren't AL.WAYS good.")

Result

Cats Aren'T Al.Ways Good.


MID$ function

Purpose

Return a portion of a .

Syntax

s$ = MID$(string_expression, start& [, length&])

Remarks

start& and length& are variables or expressions. As a function, MID$ returns a sub-string of string_expression that is length& characters long and starts at the start& character of string_expression. For example:

a$ = MID$("PowerBASIC", 1, 2)

…returns "Po". If length& is omitted, or there are fewer than length& characters to the right of the start& character of string_expression, all remaining characters of string_expression, including the start& character, are returned. If start& is greater than the length of string_expression, MID$ returns an empty string. . If length& is negative, it is interpreted as LEN(string_expression)-ABS(length&). For example, MID$("1234567890",-3) returns "890". Thus:

a$ = MID$("PowerBASIC", 4, 2) ' returns "er"

a$ = MID$("PowerBASIC", 4) ' returns "erBASIC"

a$ = MID$("PowerBASIC", 4, 20) ' returns "erBASIC"

a$ = MID$("PowerBASIC", 20) ' returns a null string

a$ = MID$("1234567890",3,-4) ' returns "345678"

If start& is negative, the starting position is assumed to be start& characters from the end of the string. MID$("abcde", -3, 2) returns "cd", the function MID$("abcde", -3) returns "cde".

Restrictions

If start& evaluates to a position outside of the string on either side, or if start& is zero, an empty string is returned. If length& is negative, all characters from start& position to the end of the string are returned.

See also

EXTRACT$, INSTR, LEFT$, LTRIM$, MID$ statement, RIGHT$, RTRIM$, TALLY, TRIM$, VERIFY


MID$ statement

Purpose

Replace characters in a with characters from another string.

Syntax

MID$(string_var, start& [, length&]) = replacement

Remarks

start& and length& are numeric variables or expressions. As a statement, MID$ replaces length& characters of string_var, beginning at character position start&, with the contents of replacement string.

If length& is included, it determines how many characters of replacement string are inserted into string_var. If length& is omitted, all of replacement string is used. If length& is negative, it is interpreted as LEN(string_var)-ABS(length&). For example, MID$("ABCDEFGHIJK",3,-7) = "*******" yields "AB****GHIJK".

If start& is negative, the starting position is assumed to be start& characters from the end of the string. MID$("abcde", -3, 2) = "123" yields "ab12e", while the statement MID$("abcde", -3) = "123" yields "ab123".

The replacement will never extend past the end of the original string_var; that is, MID$ never alters the length of a string. For a similar function that can alter the length of a string, please refer to the REPLACE statement.

Restrictions

If start& evaluates to a position outside of the string on either side, or if start& is zero, no operation is performed.

The MID$ statement cannot be used to extend the length of a string. If start is beyond the end of a string, the statement is ignored.

See also

INSTR, LTRIM$, MID$ function, REMOVE$, REPLACE, RTRIM$, TALLY, TRIM$, VERIFY

Example

DummyString$ = "1234567890"

FOR M = 1 TO 10

TestString$ = DummyString$

MID$(TestString$,1,M) = "PowerBASIC"

NEXT M

Result

P234567890

Po34567890

Pow4567890

Powe567890

...

PowerBAS90

PowerBASI0

PowerBASIC


MIN function

Purpose

Return the argument with the smallest (minimum) value.

Syntax

y = MIN(arg, arg [, arg] ...)

y& = MIN&(arg&, arg& [, arg&] ...)

y$ = MIN$(arg$, arg$ [, arg$] ...)

Remarks

These functions take any number of arguments and return the argument with the smallest (minimum) value. MIN handles arguments of any type.

MIN& handles arguments that evaluate to Integers and Long-integers (MIN& is more efficient than MIN).

MIN$ handles arguments.

If any arguments of MIN& are outside of the range of Long-integers, the result is undefined. Any arguments of MIN& will be rounded to Long-integers before the comparison begins.

MIN% is recognized as a valid synonym for MIN&.

See also

CHOOSE, CHOOSE&, CHOOSE$, IIF, IIF&, IIF$, MAX, MAX&, MAX$, SWITCH, SWITCH&, SWITCH$

Example

x& = MIN&(A, B, C, D)

x$ = MIN$("abacadabra","cad", A$, B$(4), C$ + D$ + LEFT$(E$,5))

x## = MIN(1.1@@, A%/B!, C#(x)^D, E##, SIN(F&))


MKBYT$, MKCUR$, MKCUX$, MKD$, MKDWD$, MKE$, MKI$, MKL$, MKQ$,

MKBYT$, MKCUR$, MKCUX$, MKD$, MKDWD$, MKE$, MKI$, MKL$, MKQ$, MKS$ and MKWRD$ functions

Purpose

Convert data into , ensuring that storage for a given numeric data type is consistent, regardless of its absolute value.

Syntax

DataTypeString$ = MKBYT$(byte_expr)

DataTypeString$ = MKCUR$(currency_expr)

DataTypeString$ = MKCUX$(extended_currency_expr)

DataTypeString$ = MKD$(double_precision_expr)

DataTypeString$ = MKDWD$(double_word_expr)

DataTypeString$ = MKE$(extended_precision_expr)

DataTypeString$ = MKI$(integer_expr)

DataTypeString$ = MKL$(long_integer_expr)

DataTypeString$ = MKQ$(quad_integer_expr)

DataTypeString$ = MKS$(single_precision_expr)

DataTypeString$ = MKWRD$(word_expr)

Remarks

The MKx functions return the binary representations of a number as a string value. Do not confuse these functions with the STR$ and FORMAT$ functions (which return a printable ASCII representation of a numeric expression as a string (e.g., "-42.75").

The CVx functions are complementary to the MKx functions. They convert the binary representation in a string to an actual numeric value:

Function | Converts to | From
MKBYT$ | 1-byte string | Byte
MKCUR$ | 8-byte string | Currency
MKCUX$ | 8-byte string | Extended-currency
MKD$ | 8-byte string | Double-precision
MKDWD$ | 4-byte string | Double-word
MKE$ | 10-byte string | Extended-precision
MKI$ | 2-byte string | Integer
MKL$ | 4-byte string | Long-integer
MKQ$ | 8-byte string | Quad-integer
MKS$ | 4-byte string | Single-precision
MKWRD$ | 2-byte string | Word

See also

CVI and associated functions


MKDIR statement

Purpose | Create a subdirectory/folder (like the DOS MKDIR command).
Syntax | MKDIR path$
Remarks | path$ is a string expression describing the directory to be created. MKDIR (make directory) creates the subdirectory specified by path$. If you try to create a directory that already exists, a run-time Error 75 occurs ("Path/file access error"). If path$ includes an parent folder that does not exist, a run-time Error 76 occurs ("Path not found"). MKDIR can use Long File Names (LFNs).
See also | CHDIR, RMDIR
Example | MKDIR "C:\Program Files\Company\Application Data"


MOD operator

Purpose

Return the remainder of the division between two numbers.

Syntax

p MOD q

Remarks

The MOD operator divides the two operands, p and q, and returns the remainder of that division. The result of the initial division is truncated to an value, before the remainder is calculated. See the example below.

The remainder may be a value. MOD is often considered to complement integer division.

Example

lResult1& = 10& MOD 3& ' Returns 1&

fResult2! = 13! MOD 2.7! ' Returns 2.2!

iStack& = 1023&

HiStack& = iStack& \ 256& ' Returns 3&

LoStack& = iStack& MOD 256 ' Returns 255&

' c! and d! are calculated equivalently

a! = 13

b! = 2.7

c! = a! MOD b!

d! = a! - FIX(a! / b!) * b!

CurrentLine = 1

WHILE CurrentLine < Lines

PrintLine txt$(CurrentLine)

IF (CurrentLine MOD 55) = 0 THEN DoFormFeed

INCR CurrentLine

WEND


MOUSE statement

Purpose

Turn on/off mouse event trapping and define which mouse events are to be trapped in the console input buffer.

Syntax

MOUSE {ON | OFF}

MOUSE buttons [,DOUBLE] [,MOVE] [,DOWN] [,UP]

Remarks

MOUSE ON turns on mouse event trapping and the mouse cursor. MOUSE OFF turns off mouse event trapping and the mouse cursor.

buttons is a expression which defines one or more buttons to report:

Buttons | Definition
0 | No button
1 | Left
2 | Right
4 | Second left button
8 | Third left button
16 | Fourth left button

DOUBLE is an optional parameter that defines trapping for double-click events. MOVE is an optional parameter and defines trapping for mouse movements. DOWN is an optional parameter and defines trapping for mouse button clicks. UP is an optional parameter and defines trapping for mouse button releases.

To trap for double and single clicks for up to 3 buttons on a mouse use:

MOUSE 7, DOUBLE, DOWN

All events are returned by the INKEY$ and WAITKEY$ functions. Due to limitations imposed by the Microsoft Windows operating system, it is not possible to trap the mouse wheel events in a console application.

See also

INKEY$, MOUSEBUTTONS, MOUSESTAT, MOUSEX, MOUSEY, WAITKEY$

Example

MOUSE 3, DOUBLE, DOWN

MOUSE ON

DO

i$ = WAITKEY$

SELECT CASE LEN(i$)

CASE 1

PRINT i$;" key pressed"

CASE 2

PRINT "Alt+key pressed"

CASE 4

SELECT CASE ASC(i$, 3)

CASE 2

PRINT "Double-click";

CASE 4

PRINT "Single-click";

END SELECT

PRINT " at"; MOUSEY;",";MOUSEX

END SELECT

LOOP UNTIL I$ = CHR$(27) 'ESC key


MOUSEBUTTONS function

Purpose | Return the number of buttons on the system mouse.
Syntax | buttons& = MOUSEBUTTONS
See also | INKEY$, MOUSE, MOUSESTAT, MOUSEX, MOUSEY, WAITKEY$


MOUSESTAT function

Purpose

Determine if a mouse event is waiting to be retrieved from the console input buffer.

Syntax

x& = MOUSESTAT

Remarks

Unlike INSTAT, MOUSESTAT only returns TRUE (non-zero) if a defined mouse event has occurred. It does not report keyboard events occurred, and MOUSESTAT does not remove any keyboard or mouse events from the buffer.

Once MOUSESTAT returns TRUE, it will continue to return TRUE until all mouse events are removed from the buffer using INKEY$ or WAITKEY$.

Restrictions

Mouse support must be enabled in order to test for mouse events.

See also

INKEY$, INSHIFT, MOUSE, WAITKEY$, WAITSTAT


MOUSEX function

Purpose | Return the current column position (x coordinate) of the mouse cursor, as of the most recent mouse event retrieved by INKEY$ or WAITKEY$.
Syntax | x& = MOUSEX
Remarks | The value returned is in the range of 1 to the maximum number of screen columns as returned by SCREENX.
See also | INKEY$, INSHIFT, MOUSE, MOUSESTAT, MOUSEY, WAITKEY$, WAITSTAT, SCREENX, SCREENY


MOUSEY function

Purpose | Return the current row position (y coordinate) of the mouse cursor, as of the most recent mouse event retrieved by INKEY$ or WAITKEY$.
Syntax | y& = MOUSEY
Remarks | The value returned is in the range of 1 to the maximum number of screen rows as returned by SCREENY.
See also | INKEY$, INSHIFT, MOUSE, MOUSESTAT, MOUSEX, WAITKEY$, WAITSTAT, SCREENX, SCREENY


NAME statement

Purpose | Rename a file or a directory (like the DOS REN command).
Syntax | NAME filespec1$ AS filespec2$
Remarks | The NAME statement comprises the following elements:
filespec1$ | The current name of a file or directory. The file must not be currently opened or locked. filespec1$ may be either a Short File Name (SFN) or a Long File Name (LFN).
filespec2$ | The desired name of the file or directory, and may use Long File Name (LFN) naming conventions. Each filespec may contain drive and path specifications as well as a file or directory name. If filespec1 does not exist, run-time Error 53 ("File not found") occurs. If filespec2 already exists, run-time Error 58 occurs ("File already exists"). If filespec1 has been opened or locked by your application and not closed, an Error 51 can occur ("Internal system error"). You should never rename a file that has been opened by your code and not (yet) closed.
Restrictions | It is possible to move a file from one directory, drive, or partition, to another. It is not possible to move directories between drives or partitions. Wildcard characters are not permitted in the file names.
Example | OldName$ = "MYFILE.EXE" NewName$ = "YOURFILE.EXE" NAME OldName$ AS NewName$


NOT operator

Purpose

The NOT operator works as a bitwise arithmetic operator.

Syntax

NOT p

Remarks

PowerBASIC's NOT operator returns the one's-complement of an expression. When dealing with the absolute values 0 and -1, the NOT operator "reverses" the two values, performing a Boolean-like operation.

PowerBASIC accepts any non-zero value as a logical TRUE value; therefore, subtle logic problems can arise in a program when the NOT operator is used to perform Boolean logic tests with operand values that are not limited to just 0 and -1.

Consider the following two test conditions:

test1 = 0 ' test1 is FALSE (zero)

IF NOT test1 THEN ' TRUE (-1 is non-zero)

test2 = 1 ' test2 is TRUE (1 is non-zero)

IF NOT test2 THEN ' still TRUE (-2 is non-zero)

Because NOT performs a bitwise operation on test2, it does not reverse the logical TRUE/FALSE value of test2, rather, it returns -2 (the one's-complement of 1) and this is evaluated as a logical TRUE value.

In cases where a proper logical (Boolean) evaluation is required, and the operand may be a value other than 0 and -1, the ISFALSE operator should be used in place of the NOT operator:

test3 = 1 ' test3 is TRUE (non-zero)

IF ISFALSE test3 THEN ' ISFALSE detects test3 is

... ' TRUE so the IF test fails

The two's-complement of a value can be obtained with the following algorithm:

y = (NOT x) + 1

Using NOT as a logical operator

NOT returns 0 (FALSE) if and only if its operand is exactly -1 (TRUE). Generally, you should use the ISFALSE operator instead of NOT, when you are testing for logical falsity.

Truth table
x | NOT x
0 | -1
-1 | 0

Using NOT as a bitwise arithmetic operator

NOT performs a one's-complement or bit reversal of each bit in an integer-class value. Here is a sample:

x% = NOT 16383% ' Result is –16384

See also

Arithmetic Operators, AND, EQV, IMP, ISFALSE, ISTRUE, OR, Short-circuit evaluation, XOR


NUL$ function

Purpose

Return a consisting of a specified number of $NUL (CHR$(0)) characters.

Syntax

sResult$ = NUL$(count)

Remarks

NUL$ returns a count character string of $NUL characters.

See also

CHR$, REPEAT$, SPACE$, STRING$


OBJACTIVE function

Purpose | Return TRUE/FALSE as an indication of the running state of an initialized COM object (EXE based).
Syntax | lResult& = OBJACTIVE(progid$)
Remarks | OBJACTIVE can provide information that may prove useful in determining whether to use the NEW option with the LET or SET statement, or omit it to use an existing instance of a COM object. OBJACTIVE may only be used on COM objects that are in EXE format, not DLL/OCX/etc. In the latter case, OBJACTIVE returns FALSE (0).
progid$ | The registered program ID string for the COM object. For example, "Word.Application.8", or a version-independent program ID such as "Word.Application". A valid program ID string can be obtained from a 16-byte class ID string using the PROGID$ function, or derived from a 38 character GUID string using PROGID$ and GUID$.
See also | DIM, CLSID$, GUID$, GUIDTXT$, INTERFACE/END INTERFACE, ISNOTHING, ISOBJECT, OBJECT, OBJPTR, OBJRESULT, PROGID$, SET
Example | ' Create a reference to the MSWORD object DIM oWord AS DISPATCH ' use late-binding IF OBJACTIVE("Word.Application") THEN ' Already active, use existing instance LET oWord = DISPATCH IN "Word.Application" ELSE ' No server active, create a new instance LET oWord = NEW DISPATCH IN "Word.Application" END IF ' Obtain a progid from a 38-byte GUID, and attempt to reference the object instance DIM A AS STRING A = "{000209FF-0000-0000-C000-000000000046}" A = PROGID$(GUID$(A)) ' A now contains "word.application.8" IF OBJACTIVE(A) THEN...


OBJECT statement

Purpose

Communicate with a COM object through the dispatch interface.

Syntax

OBJECT GET interface.member[.member…] [([[paramname =] vrntparam1 _

[, ...]])] TO vrntresult

OBJECT LET interface.member[.member…] [([[paramname =] vrntparam1 _

[, ...]])] = vrntvalue

OBJECT SET interface.member[.member…] [([[paramname =] vrntparam1 _

[, ...]])] = vrntvalue

OBJECT CALL interface.member[.member…] [([[paramname =] vrntparam1 _

[, ...]])] [TO vrntresult]

Remarks

There are four general forms of the OBJECT statement which are used to communicate through an exposed COM interface to a COM object.

OBJECT GET | Retrieve or read the value of an Interface member Property. This is similar to retrieving the value of a variable.
OBJECT LET | Assign or write a value to an Interface member Property. This is similar to assigning a value to a variable.
OBJECT SET | Assign or write a value to an Interface member Property that contains a reference to an object. For example, a reference to another Interface.
OBJECT CALL | Call or execute a member Method of an Interface. This is equivalent to calling a Sub or Function.

All parameters, return values, and assignment values must be in the form of Variant variables. Dispatch object Method calls are bound at run-time, and require no declaration of Properties and Methods. However, for this reason, the validity of these references is also not checked at compile-time.

The OBJECT statement can use both positional and named parameters, but you should keep in mind that while most COM Dispatch Servers support named parameters, it is not universal.

A positional parameter is simply a Variant variable containing an appropriate value. A named parameter consists of a parameter identifier, an equal (=) sign, and a Variant variable containing an appropriate value. Positional parameters must precede any and all named parameters, but named parameters may be specified in any sequence.

Restrictions

All parameters, return values, and assignment values must be in the form of variant variables. If an error occurs during an OBJECT statement, the ERR system variable is set. An extended COM error code can be retrieved from the OBJRESULT variable, and this can be useful for both debugging and handling run-time errors.

Using the wrong member mode (GET/LET/SET/CALL) can result in unexpected and fatal run-time errors in some situations. For example, a member method (OBJECT CALL) is expected to return an Interface reference via a variant return variable, but a programming error specifies an OBJECT GET statement instead. When used correctly, the variant variable would contain the Interface reference as the return result, but because of the programming and subsequent run-time error that occurs, the variant's content is considered undefined. At this point, attempting to assign the undefined variant to an object variable will likely trigger a General Protection Fault (GPF).

So, test the result code in OBJRESULT and ERR after an OBJECT statement, in order to detect such run-time failures.

See also

DIM, CLSID$, GUID$, GUIDTXT$, INTERFACE/END INTERFACE, ISNOTHING, ISOBJECT, OBJACTIVE, OBJPTR, OBJRESULT, PROGID$, SET

Example

' (Assumes Interface definitions have been declared

' for the IVSSDatabase and IVSSItem Interfaces)

DIM Ini AS VARIANT

DIM User AS VARIANT

DIM Password AS VARIANT

DIM tVnt1 AS VARIANT

DIM tVnt2 AS VARIANT

DIM tVnt3 AS VARIANT

' Use early-binding to IVSSDatabase and IVSSItem

DIM oVss1 AS IVSSDatabase ' Interface name

DIM oVss2 AS IVSSItem ' Interface name

' Create a new instance of the COM Object

LET oVss1 = NEW IVSSDatabase IN $COMServer

IF ISOBJECT(oVss1) = 0 THEN EXIT FUNCTION

' Log in to the Database

Ini = ""

User = "MyUserName"

Password = "MyPassword

OBJECT CALL oVss1.Open(Ini, User, Password)

IF OBJRESULT <> 0 THEN GOTO ErrorHandler

' Get an Interface reference for the root item

tVnt1 = "$/"

OBJECT GET oVss1.VSSItem(spec=tVnt1) TO tVnt2

IF OBJRESULT <> 0 THEN GOTO ErrorHandler

' Assign the Interface to an object variable

LET oVss2 = tVnt2

IF ISOBJECT(oVss2) = 0 THEN GOTO ErrorHandler

' Get the .Count Property value from the Interface's .Items() member Property

tVnt3 = 0

OBJECT GET oVss2.Items(tVnt3).Count TO tVnt3

IF OBJRESULT <> 0 THEN GOTO ErrorHandler

' Convert the result to a non-Variant variable

Count& = VARIANT#(tVnt3)


OBJPTR function

Purpose | Return an object pointer contained in the specified object variable.
Syntax | ObjectPointer??? = OBJPTR(objectvar)
Remarks | OBJPTR returns the object pointer as a Double-word (DWORD) value.
See also | DIM, CLSID$, GUID$, GUIDTXT$, INTERFACE/END INTERFACE, ISNOTHING, ISOBJECT, OBJECT, OBJACTIVE, OBJRESULT, PROGID$, SET


OBJRESULT function

Purpose

Return the result of the execution of the most recent OBJECT statement.

Syntax

lResult& = OBJRESULT

Remarks

If an OBJECT statement fails at run-time, the ERR system variable is set to an appropriate PowerBASIC error code. This is usually Error 5 ("Illegal function call"). In such cases, you can use the OBJRESULT function to return the result (lResult&) of the last run-time OBJECT statement.

Numeric equates for most OBJRESULT errors can be found in the WIN32API.INC file, and are mostly prefixed with %E_, %CO_ and %OLE_. The following list comprises a selection of the most common errors that may be encountered:

%S_OK | = &H0
%S_FALSE | = &H1
%E_UNEXPECTED | = &H8000FFFF&
%E_NOTIMPL | = &H80004001&
%E_NOINTERFACE | = &H80004002&
%E_POINTER | = &H80004003&
%E_ABORT | = &H80004004&
%E_FAIL | = &H80004005&
%E_ACCESSDENIED | = &H80070005&
%E_HANDLE | = &H80070006&
%E_OUTOFMEMORY | = &H8007000E&
%E_INVALIDARG | = &H80070057&

As can be seen from the above error list, a large numeric error code can be cryptic. However, we can translate the OBJRESULT error code into a verbose error message using the FormatMessage API function. This can be most helpful especially during application development and debugging.

See also

DIM, CLSID$, GUID$, GUIDTXT$, INTERFACE/END INTERFACE, ISNOTHING, ISOBJECT, OBJECT, OBJACTIVE, OBJPTR, PROGID$, SET

Example

' Uses MAKELANGID() function in WIN32API.INC

DIM A AS ASCIIZ * 1024

CALL FormatMessage(%FORMAT_MESSAGE_FROM_SYSTEM, _

BYVAL 0&, OBJRESULT, BYVAL MAKELANGID( _

%LANG_NEUTRAL, %SUBLANG_DEFAULT), _

A, SIZEOF(A), BYVAL 0&)

' A will contain a verbose system error message


OCT$ function

Purpose

Return a that is the octal (base 8) representation of its argument.

Syntax

s$ = OCT$(numeric_expression [, digits])

Remarks

numeric_expression must be in the range -2,147,483,648 to +4,294,967,295. Any fractional part of numeric_expression is rounded before the string is created.

If digits is specified, the result will be of the length digits. If the value is shorter than digits, leading zero will be supplied to pad the string. If the value is longer than digits, the result will be truncated from the left. digits may range from 1 to 11. If digits is zero, no padding is used. If digits is less than zero, PowerBASIC pads the string to 11 digits.

Octal is a number system that uses base 8, rather than the base 10 used by the ordinary decimal system. A single octal digit represents three bits. Octal notation was commonly used in programming, because it was a convenient way of representing the binary bit patterns used internally by computers. Octal has largely been replaced by hexadecimal on modern computers, due to the more convenient size of hexadecimal notation. However, octal is still often used in Unix-oriented operating systems, and in C code, typically for historical reasons.

By convention, octal numbers are unsigned, since they represent bit patterns. If you are not familiar with two's-complement arithmetic, the result of using OCT$ on a negatively signed value may surprise you. See the BIN$ function entry for information about two's-complement arithmetic.

Octal strings can be converted to numeric values with the VAL function by prefixing the string with "&O", "&Q" or just "&". If the string has a leading zero, the result is always unsigned. For example:

a$ = OCT$(65535) ' a$ contains "177777"

x& = VAL("&Q" + a$) ' Signed result (-1)

y& = VAL("&Q0" + a$) ' Unsigned result (65535)

See also

BIN$, FORMAT$, HEX$, STR$, USING$, VAL


ON ERROR statement

Purpose

Specify an error handling routine; enable or disable error trapping.

Syntax

ON ERROR GOTO {label | line_number}

ON ERROR RESUME NEXT

ON ERROR GOTO 0

Remarks

label or line_number identifies the first line of the error trapping routine. Once error handling has been turned on with this statement, all run-time errors result in a jump to your error handling code. You must always use the RESUME statement to continue execution once the error has been handled.

To disable error trapping, use ON ERROR GOTO 0 or ON ERROR RESUME NEXT. You can use this technique if an error occurs for which you have not defined a recovery path; you can also choose to display the contents of ERR or ERRCLEAR at this time.

The default for error trapping is disabled. If an error occurs while error trapping is disabled, the error code is placed into the ERR system variable, and execution continues. Errors can still be trapped by checking the value of the ERR or ERRCLEAR variable with ERR THEN or SELECT CASE ERR statements.

Error trapping is local to each Sub and Function. PowerBASIC does not support global error trapping.

Numeric errors such as Divide-by-zero, Overflow and Underflow are not trapped. Array out-of-bounds and null-pointer trapping are only enabled if #DEBUG ERROR ON is used.

If you're running a program with error trapping turned off, a run-time error may cause a General Protection Fault (GPF). A GPF cannot be trapped with ON ERROR.

See also

#DEBUG ERROR, ERR, ERRCLEAR, ERROR, Error Overview, ERROR$, Errors and Error Trapping, RESUME, Run-time Errors


ON GOSUB statement

Purpose

Call one of several subroutines according to the value of a numeric expression.

Syntax

ON n GOSUB {label | line_number} [, {label | line_number}] ...

Remarks

n is a numeric expression ranging from 1 to 255, and each label or line number identifies a statement to branch to. When this statement is encountered, the nth label in the list is branched to; for example, if n equals 4, the fourth label in the list receives control. If n is less than one or greater than the number of labels, no branch occurs, and PowerBASIC continues execution with the statement immediately following the ON GOSUB statement.

Each subroutine should end with RETURN, which causes execution to resume with the statement immediately following the ON GOSUB statement. ON GOSUB can only branch to labels or line numbers that have the same scope as the ON GOSUB statement.

The SELECT and IF blocks also perform multiple branching and are more flexible than ON GOSUB.

Note that ON GOSUB (and ON GOTO) have been internally optimized to produce greater run-time performance than was possible with previous versions of PowerBASIC.

See also

GOSUB, FUNCTION/END FUNCTION, IF block, ON GOTO, SELECT, SUB/END SUB

Example

FOR I& = 1 TO 3

ON I& GOSUB OneHandler, TwoHandler, ThreeHandler

NEXT I&

OneHandler:

Message$ = "Handler number" + STR$(I&)

RETURN

TwoHandler:

Message$ = "Handler number" + STR$(I&)

RETURN

ThreeHandler:

Message$ = "Handler number" + STR$(I&)

RETURN

Result

Handler number 1

Handler number 2

Handler number 3


ON GOTO statement

Purpose

Send program flow to one of several possible destinations based on the value of a numeric expression.

Syntax

ON n GOTO {label | line_number} [, {label | line_number}] ...

Remarks

n is a numeric expression ranging from 1 to 255, and label or line_number identifies a statement in the program to branch to. The nth label is branched to; for example, if n equals 4, the fourth label in the list receives control. If n is less than one or greater than the number of labels in the list, program execution continues with the statement that immediately follows the ON GOTO statement.

ON GOTO behaves exactly like ON GOSUB, except that it performs a GOTO rather than a GOSUB. This means that the program retains no memory of where the branch originated. ON GOTO can only branch to labels or line numbers that have the same scope as the ON GOTO statement.

The SELECT and IF blocks also perform multiple branching, and are more flexible than ON GOTO. See the GOTO entry for a discussion of ways to avoid using GOTOs in your programs.

Note that ON GOTO (and ON GOSUB) have been internally optimized to produce greater run-time performance than was possible with previous versions of PowerBASIC.

See also

GOTO, IF block, ON GOSUB, SELECT

Example

SUB Main

FOR I& = 1 TO 3

ON I& GOTO OneHandler, TwoHandler, ThreeHandler

Back:

NEXT I&

EXIT SUB

OneHandler:

Message$ = "Handler number" + STR$(I&)

GOTO Back

TwoHandler:

Message$ = "Handler number" + STR$(I&)

GOTO Back

ThreeHandler:

Message$ = "Handler number" + STR$(I&)

GOTO Back

END SUB

Result

Handler number 1

Handler number 2

Handler number 3


OPEN statement

Purpose

Prepare a file or device for reading or writing.

Syntax

OPEN filespec [FOR mode] [ACCESS access] [LOCK lock] AS _

[#] filenum& [LEN = record_size] [BASE = base]

OPEN HANDLE filehandle [FOR mode] [ACCESS access] [LOCK lock] AS _

[#] filenum& [LEN = record_size] [BASE = base]

Remarks

The OPEN statement comprises the following elements:

filespec

A string expression specifying the name of the file to be opened, and may optionally include a drive and/or path specification. filespec may be either a Short File Name (SFN) or a Long File Name (LFN). filespec has a limit of 259 characters (%MAX_PATH - 1), although the file name portion of filespec may be no more than 255 characters (%MAX_FNAME - 1).

mode

Specifies the file organization and style of access (sequential, random access, or binary) for reading, writing (or both), or appending. If mode is not specified, the default is RANDOM access.

Mode | File type | Action
INPUT | Sequential | Read from
OUTPUT | Sequential | Write to
APPEND | Sequential | Append to
BINARY | Binary | Reading or writing
RANDOM | Random | Reading or writing (default)

access

Specifies the type of access this process will have to the file. By default, the file may be written to and read from.

Access | Description
READ | Only read operations allowed
WRITE | Only write operations allowed
READ WRITE * | Both read and write operations allowed (default)

* If you explicitly specify an ACCESS clause in an OPEN statement for a file opened in APPEND mode, the ACCESS clause must be READ WRITE.

lock

Specifies the type of access other processes will have to the file. If a LOCK clause is not specified in the OPEN statement, the default LOCK READ WRITE mode is applied. This mode ensures exclusive access to the file, and enables PowerBASIC to optimize its internal buffering for utmost I/O performance. If other processes or threads are to be permitted WRITE access to the file (LOCK SHARED or LOCK READ), internal buffering is disabled. Whilst performance may be marginally lower, it ensures that data read from the file is completely up-to-date.

lock | Description
LOCK SHARED | Both read and write operations allowed
LOCK WRITE | Prevent write operations
LOCK READ | Prevent read operations
LOCK READ WRITE | Neither read nor write operations allowed (default)

To open a text file for OUTPUT and allow other processes to only read the file, use the following:

OPEN "MYFILE.TXT" FOR OUTPUT LOCK WRITE AS #1

It is possible for an application to open more than one copy of a given file at the same time. In this case, each OPEN statement must use a unique file number, and LOCK READ WRITE mode should not be used.

filenum&

A unique integer value identifying the file, in the range 1 to 32767. Typically, this value is obtained from the FREEFILE function.

record_size

Specifies the size of each record of a random access file. The default record length is 128 if not specified. If record_size is specified for a sequential file, it instructs PowerBASIC to use internal buffering to improve I/O performance. A random access file is limited to 32768 bytes per record, to ensure consistent behavior across all Win32 platforms.

base

Specifies the number of the first record in a random access file, or the number of the first byte in a sequential or binary file. It can be either zero (0) or one (1). The default value for base is 1, if not specified.

The main function of OPEN is to associate a file number (filenum&) with a file or physical device and to prepare that device for reading and/or writing. This file number is then used, rather than its name, in every statement that refers to the file. The FREEFILE function can be used to determine the next available file number, or you can pick one yourself. The OPEN statement contains information on the mode of the file; that is, the methods by which the file will be accessed: sequential (for input/output to a new file, or output to an existing file), random access, and binary. An OPEN statement is usually balanced by a matching CLOSE statement.

HANDLE

The HANDLE option allows you to access files that have already been opened by another process, DLL, or API function. The filehandle specified here must be a valid Win32 operating system file handle.

When PowerBASIC closes a file opened with OPEN HANDLE, the Win32 handle is simply detached from the internal PowerBASIC handle table. The file is not physically closed since PowerBASIC did not originally open it. In PowerBASIC, the FILEATTR function can be used to obtain the operating system file handle for a file opened with the OPEN statement.

Restrictions

Attempting to OPEN a file for INPUT that does not exist causes a run-time Error 53 ("File not found"). Attempting to open a file that is locked can result in either an Error 70 ("Permission denied"), or an Error 75 ("Path/file access error").

Similarly, attempting to OPEN a file using a file number that is already in use will result in a run-time Error 55 ("File is already open "). For this reason, programs that use hard-coded file numbers should take special care to close files before the file number is used again. In addition, code that may be used by more than one thread should use FREEFILE and avoid hard-coded file numbers.

If you try to open a nonexistent file for OUTPUT, APPEND, RANDOM, or BINARY operations, a new file is automatically created. For this reason, files on Read-only network drives may only be opened in INPUT mode.

See also

CLOSE, FILEATTR, FILENAME$, FILESCAN, FREEFILE, TCP OPEN, UDP OPEN

Example

This program is divided into five procedures. The difference between each procedure is the mode in which the file is opened, and the way the data in the file is manipulated:

SUB SequentialOutput

' The file is opened for sequential output,

' and some data is written to it. If the file

' exists, it is over-written.

OPEN "OPEN.DTA" FOR OUTPUT AS #1

IntegerVar% = 12345

TempStr$ = "History is made at night."

WRITE #1, TempStr$, IntegerVar%*2, TempStr$, IntegerVar% \ 2

CLOSE #1

END SUB ' end procedure Sequential Output

SUB SequentialAppend

' The file is opened for sequential output, and

' data in this case is added to the end of file.

' If the file does not exist, it is created.

OPEN "OPEN.DTA" FOR APPEND AS #1

IntegerVar% = 32123

TempStr$ = "I am not a number!"

WRITE #1, TempStr$, IntegerVar% * 0.2

CLOSE #1

END SUB ' end procedure Sequential Append

SUB SequentialInput

' The file is opened for sequential input,

' and data is read from the file.

DIM a$

OPEN "OPEN.DTA" FOR INPUT AS #1

LINE INPUT #1, TempStr$

TempStr$ = ""

WHILE ISFALSE EOF(1) ' check if at end of file

LINE INPUT #1, a$

TempStr$ = TempStr$ + a$

WEND

CLOSE #1

END SUB ' end procedure SequentialInput

SUB BinaryIO

' The file is opened for binary I/O. Data is

' read 'using GET$. SEEK explicitly moves the

' file pointer to 'the end of file, and the

' same data is written back to 'the file.

OPEN "OPEN.DTA" FOR BINARY AS #1

TempStr$ = ""

WHILE ISFALSE EOF(1)

GET$ #1, 1, Char$

TempStr$ = TempStr$ + Char$

WEND

SEEK #1, LOF(1)

FOR I& = 1 TO LEN(TempStr$)

PUT$ #1, MID$(TempStr$,I&,1)

NEXT I&

CLOSE 1

END SUB ' end procedure BinaryIO

SUB RandomIO

' Open file for random I/O. GET and PUT read

' and write the data.

OPEN "OPEN.DTA" FOR RANDOM AS #1 LEN = 1

TempStr$ = ""

TempSize& = LOF(1) ' save file size

' using GET, read in the entire file

FOR I& = 1 TO TempSize&

GET #1, I&, Char$

TempStr$ = TempStr$ + Char$

NEXT I&

' PUT copies the data in reverse into the

' random access file.

SEEK #1, 1

FOR I& = TempSize& TO 1 STEP -1

LSET Char$ = MID$(TempStr$,I&,1)

PUT #1,, Char$

NEXT I&

CLOSE #1

END SUB ' end procedure RandomIO


OPTION EXPLICIT statement

Purpose

Force explicit declaration of all variables.

Syntax

OPTION EXPLICIT

Remarks

Using OPTION EXPLICIT in a program has the same effect as using the #DIM ALL metastatement. That is, it requires that all variables be declared before they are used.

When this option is used, the compiler generates a compile-time error if a variable or array is used without being explicitly declared.

See also

#DIM


OR operator

Purpose

The OR operator works as both a logical and a bitwise arithmetic operator.

Syntax

p OR q

Remarks

Using OR as a logical operator

OR returns TRUE (non-zero) if and only if either or both of its operands is TRUE. Here is OR's truth table:

Truth table
x | y | x OR y
T | T | T
T | F | T
F | T | T
F | F | F

Using OR as a bitwise arithmetic operator

An OR mask sets selected bits of an value without affecting the other bits. To set the most significant 2 bits in &H9700, use OR with a mask of &HC000; that is, the mask contains all 0s, except for the bit positions you wish to force to 1:

See also

Arithmetic Operators, AND, EQV, IMP, ISFALSE, ISTRUE, NOT, XOR


PAGE statement

Purpose

Select which of 8 console text pages (screens) is active and/or visible.

Syntax

PAGE [active] , [visible]

Remarks

The active page is the page where console output is created using the PRINT, LOCATE and CLS statements, etc. The visible page is the page currently being displayed. Both parameters are optional although the comma is not. If specified, each parameter must be in the range of 1 to 8 inclusive. If either parameter is omitted, the previous page number for the missing parameter remains in effect. If either parameter is invalid, the entire operation is ignored.

Using separate active and visible pages provides a mechanism for application screens to be constructed in the background, and copied to the visible page (using PCOPY) for instant screen updates.

If redirection is not in effect, STDOUT writes its output to page 1 regardless of the current PAGE settings.

Using two or more console pages is a simple yet very effective technique to vastly speed up the screen display in Windows 95, 98, and ME. See the example code below.

See also

CLS, LOCATE, PAGEACTIVE, PAGEVISIBLE, PCOPY, PRINT, STDOUT

Example

FUNCTION PBMAIN

CLS

PAGE 2,1

FOR x& = 65 TO 90

PRINT STRING$(SCREENX * SCREENY, x&);

PCOPY 2,1

NEXT x&

PAGE 1,1

WAITKEY$

END FUNCTION


PAGEACTIVE function

Purpose

Return the number of the active console page.

Syntax

apage& = PAGEACTIVE

Remarks

PAGEACTIVE returns the current active console page number in the range 1 to 8 inclusive, as set by the PAGE statement.

The output from the PRINT statement is displayed on the active console page, and is only immediately visible if the visible console page is the same as the active console page, or the text is subsequently copied from the active to the visible page. Use the PCOPY statement to copy text from one console page to another.

See also

PAGE, PAGEVISIBLE, PRINT, STDOUT


PAGEVISIBLE function

Purpose

Return the number of the visible console page.

Syntax

vpage& = PAGEVISIBLE

Remarks

PAGEVISIBLE returns the current visible console page number in the range 1 to 8 inclusive, as set by the PAGE statement.

The output from the PRINT statement is displayed on the active console page, and is only immediately visible if the visible console page is the same as the active console page, or the text is subsequently copied from the active to the visible page. Use the PCOPY statement to copy text from one console page to another.

See also

PAGE, PAGEACTIVE, PRINT, STDOUT


PARSE$ function

Purpose

Return a delimited field from a string expression.

Syntax

a$ = PARSE$(string_expr [, {[ANY] string_delimiter | BINARY}], index&)

Remarks

PARSE$ uses the following parameters:

string_expr

The to parse. If string_expr is empty (a null string) or contains no delimiter character(s), the string is considered to contain exactly one field. In this case, PARSECOUNT will return the value 1.

string_delimiter

Contains delimiter character(s). A delimiter is a character, list of characters, or string, that is used to mark the end of a field in string_expr. For example, if you consider a sentence to be a list of words, the delimiter between the works is a space (or perhaps punctuation). Text files typically consist of lines that are delimited by CR/LF ($CRLF or CHR$(13,10)) characters; a database file may consist of items separated by commas; etc. A delimiter is not considered part of a field, but as the divider between fields, so the delimiter is never returned by PARSE$.

If the ANY keyword is used, string_delimiter contains a set of characters, any of which may act as a delimiter character. If the ANY keyword is omitted, the entire string_delimiter string acts as a single delimiter. Delimiters are case-sensitive, so capitalization may be a consideration.

If string_delimiter is not specified, or contains an empty string, special rules apply. The delimiter is assumed to be a comma. Fields may optionally be enclosed in quotes. Such quotes are considered as special field delimiters and are not returned as part of the result string. Any characters that appear between a quote mark and the next comma delimiter character are discarded. If no leading quote is found, any leading or trailing spaces are trimmed before the result string is returned.

BINARY

The BINARY option presumes that string_expr was created with the JOIN$/BINARY function, or its equivalent, which creates a string as a binary image or in the PowerBASIC and/or Visual Basic packed string format: If a string is shorter than 65535 bytes, it starts with a 2-byte length WORD followed by the string data. Otherwise it will start a 2-byte value of 65535, followed by a DWORD indicating the string length, then finally the string data itself.

index&

An variable or expression that specifies the delimited field number to return. The first field is 1, and so on up to the maximum number of fields contained in string_expr, which may be determined with the PARSECOUNT function. If index& is negative, string_expr is parsed from right to left. In this case, index& = -1 returns the last field in string_expr, -2 returns the second to last, etc. If index& evaluates to zero, or is outside of the actual field count, an empty string is returned.

See also

JOIN$, PARSE, PARSECOUNT

Example

a$ = PARSE$("one,two,three", 2) ' returns "two"

a$ = PARSE$("one;two,three", 2) ' returns "three"

a$ = PARSE$("one",2) ' returns ""

a$ = PARSE$("xyz",1) ' returns "xyz"

a$ = PARSE$("xx1x","x",3) ' returns "1"

a$ = PARSE$("1;2,3", ANY ",;", 2) ' returns "2"


PARSE statement

Purpose

Parse an entire and extract all delimited fields into an array.

Syntax

PARSE main$, array$() [, {[ANY] delim$ | BINARY}]

Remarks

PARSE parses the entire string or string expression specified by main$, assigning each delimited sub-string to successive elements of array$. The array specified by array$ may be a dynamic string array, a fixed-length string array, an ASCIIZ string array, or a .

The field delimiter is defined by delim$, which may be one or more characters long. To be valid, the entire delimiter must match exactly, but the delimiter itself is never assigned as a part of the delimited field.

If delim$ is not specified or is null (zero-length), standard comma-delimited (optionally quoted) fields are presumed. In this case only, the following parsing rules apply. If a standard field is enclosed in optional quotes, they are removed. If any characters appear between a quoted field and the next comma delimiter, they are discarded. If no leading quote is found, any leading or trailing blank spaces are trimmed before the field is returned.

ANY

If the ANY option is chosen, each appearance of any single character comprising delim$ is considered a valid delimiter.

BINARY

The BINARY option presumes that the string_expr was created with the JOIN$/BINARY function, or its equivalent, which creates a string as a binary image or in the PowerBASIC and/or Visual Basic packed string format: If a string is shorter than 65535 bytes, it starts with a 2-byte length WORD followed by the string data. Otherwise it will start a 2-byte value of 65535, followed by a DWORD indicating the string length, then finally the string data itself.

It is usually advantageous to dimension array$ to the correct size with the use of the PARSECOUNT function. The PARSE statement is typically much more efficient, as a whole, than repeated use of the PARSE$ function when it is necessary to parse an entire string expression.

The JOIN$ function is the natural complement to the PARSE statement.

See also

JOIN$, PARSE$, PARSECOUNT

Example

a$ = "Trevor, Bob, Bruce, Dan, Simon, Jenny"

DIM b$(1 TO PARSECOUNT(a$))

PARSE a$, b$()

ARRAY SORT b$()

Result

b$(1) = "Bob"

b$(2) = "Bruce"

b$(3) = "Dan"

b$(4) = "Jenny"

b$(5) = "Simon"

b$(6) = "Trevor"


PARSECOUNT function

Purpose

Return the count of delimited (sub) fields in a string expression.

Syntax

x& = PARSECOUNT(string_expr [, {[ANY] string_delimiter | BINARY}])

Remarks

PARSECOUNT uses the same rules as PARSE$ in the determination of fields within string_expr. Individual fields within string_expr are evaluated, and the tally of the fields forms the result value.

string_expr

is the to parse. If string_expr is empty (a null string) or contains no delimiter character(s), the string is considered to contain exactly one field. In this case, PARSECOUNT returns the value 1.

string_delimiter

defines one or more characters to use as a delimiter. To be valid, the entire delimiter must match exactly, but the delimiter itself is never returned as part of the field.

If string_delimiter is not specified, or contains an empty string, special rules apply. The delimiter is assumed to be a comma. Fields may optionally be enclosed in quotes, and are ignored before the result string is returned. Any characters that appear between a quote mark and the next comma delimiter character are discarded. If no leading quote is found, any leading or trailing quotes are trimmed before the result string is returned.

If string_expr is empty (a null string), PARSECOUNT will return 1, indicating that one empty (null) field exists. Similarly, if string_expr does not contain any delimiters, string_expr is considered to contain one field.

BINARY

The BINARY option returns the number of sub-fields and presumes that string_expr was created with the JOIN$/BINARY function, or its equivalent, which creates a string as a binary image or in the PowerBASIC and/or Visual Basic packed string format: If a string is shorter than 65535 bytes, it starts with a 2-byte length WORD followed by the string data. Otherwise it will start a 2-byte value of 65535, followed by a DWORD indicating the string length, then finally the string data itself.

See also

JOIN$, PARSE, PARSE$

Example

a& = PARSECOUNT("one,two,three") ' returns 3

a& = PARSECOUNT("one;two,three") ' returns 2

a& = PARSECOUNT("") ' returns 1

a& = PARSECOUNT("xx1x","x") ' returns 4

a& = PARSECOUNT("1;2,3", ANY ",;") ' returns 3


PBMAIN function

Purpose

PBMAIN is a user-defined function called by Windows to begin running an executable application. Every PowerBASIC executable (EXE) must contain either a PBMAIN or a WINMAIN function.

Syntax

FUNCTION PBMAIN [()] [AS LONG]

Remarks

Either a PBMAIN or WINMAIN function is required in every PowerBASIC application (.EXE). If you use PBMAIN, no parameters are passed, and you cannot directly obtain the instance handle of your application or the to any command-line parameters.

However, you can use COMMAND$ to get the command-line passed to your program, and the GetModuleHandle API function to get the application instance handle.

Return

The return value of PBMAIN has an effective range of 0 to 255. Batch files may act on the result through the IF [NOT] ERRORLEVEL batch command.

See also

COMMAND$, WINMAIN

Example

#COMPILE EXE

FUNCTION PBMAIN

PRINT "This is my program!"

'Return an error level of 15

FUNCTION = 15 ' or you can use PBMAIN = 15

END FUNCTION


PCOPY statement

Purpose

Copy all text from one console text page (screen) to another.

Syntax

PCOPY source, destination

Remarks

source is an value from 1 to 8 (inclusive) that specifies the number of the screen page to be copied. If either parameter is invalid, the entire operation is ignored.

destination is an integer value in the range 1 to 8 (inclusive) that specifies the number of the screen page to which the source page is copied. Text and color attributes on the destination page are replaced by the text and color attributes present on the source page. Use the PAGE statement to alter the active and visible screen pages.

Using two or more console pages is a simple yet very effective technique to vastly speed up the screen display in Windows 95, 98, and ME. See the example code below.

If redirection is not in effect, STDOUT writes text to page 1, and this can copied to the visible page by using PCOPY 1, destination.

See also

CLS, LOCATE, PAGE, PRINT, STDOUT

Example

FUNCTION PBMAIN

CLS

PAGE 2,1

FOR x& = 65 TO 90

PRINT STRING$(SCREENX * SCREENY, x&);

PCOPY 2,1

NEXT x&

PAGE 1,1

WAITKEY$

END FUNCTION


PEEK and PEEK$ functions

Purpose

Return the byte (PEEK) or sequence of bytes (PEEK$) at a specified memory location.

Syntax

numvar = PEEK([datatype,] address???)

strvar = PEEK$([ASCIIZ,] address???, count&)

Remarks

The PEEK functions and complementary POKE statements are low-level methods of accessing individual bytes in memory. The data is retrieved from memory, starting at the specified 32-bit address???.

PEEK retrieves a value starting at a specified memory address.

PEEK$ retrieves count& consecutive bytes and returns them as a , where the ASCII code of the first character of the string is the value of the first byte retrieved, the next ASCII code is the next byte retrieved, and so on. If ASCIIZ is specified, PEEK$ reads successive characters from an ASCIIZ buffer of the specified size, until a terminating $NUL (CHR$(0)) byte is found. Since ASCIIZ strings must contain a terminating $NUL, the maximum length of the returned string is 1 character less than the specified size.

datatype

The numeric data type to retrieve, which may be any one of BYTE, WORD, DWORD, INTEGER, LONG, QUAD, SINGLE, DOUBLE, EXT, CUR, CUX. If a data type is not specified, BYTE is assumed.

address???

A valid 32-bit memory address specifying the location in memory where data retrieval should begin.

count&

A numeric expression that specifies the number of consecutive bytes to be read from memory starting at address???.

Restrictions

If address??? (or any byte in the range covered by count&) references an invalid address (memory that is not allocated to the application), Windows will generate a General Protection Fault (GPF) and terminate the application. GPFs cannot be trapped with an ON ERROR error handler.

See also

, POKE, STRPTR, VARPTR

Example

One common application for PEEK$ and POKE$ is to perform fast array and memory block copy operations by simply copying the entire block of memory which contains the array data, rather than storing each element individually with an assignment statement:

Elements& = 2000 ' 2000 elements in each array

DIM OriginalArray%(1 TO Elements&)

DIM NewArray%(1 TO Elements&)

'Method 1: assign each element individually

FOR Index& = 1 TO Elements&

NewArray%(Index&) = OriginalArray%(Index&)

NEXT Index&

'Method 2: block copy with PEEK$ and POKE$ (faster)

Source& = VARPTR(OriginalArray%(1))

Dest& = VARPTR(NewArray%(1))

ArrayLen& = Elements& * 2 'byte length of array

POKE$ Dest&, PEEK$(Source&, ArrayLen&) 'copy block


POKE and POKE$ statements

Purpose

Store the byte (POKE), or sequence of bytes (POKE$) at a specified memory location.

Syntax

POKE [datatype,] address???, datavalue

POKE$ [ASCIIZ,] address???, string_expr

Remarks

The POKE statements and complementary PEEK functions are low-level methods of accessing individual bytes at a specific address in memory. The data is stored to memory starting at location address???, which is a full 32-bit address.

In its classic form, the POKE statement stores a single byte (8 bits) whose value ranges from 0 to 255. In its enhanced form, POKE provides the functionality of a dynamic pointer: the datatype parameter specifies the data type and hence the size of the target data to write to the target memory address. datatype can be any one of BYTE, WORD, DWORD, INTEGER, LONG, QUAD, SINGLE, DOUBLE, EXT, CUR, CUX.

POKE$ stores a in consecutive bytes where the ASCII code of the first character of the string is stored in the first byte of memory, the next ASCII code is stored in the next byte of memory, and so on, until all bytes in string_expr are stored. If ASCIIZ is specified, POKE$ writes successive characters to a target address in memory until a terminating $NUL byte is found in the source string. If no $NUL is found in the string, one is automatically appended to the target buffer. It is the programmer’s responsibility to ensure that POKE$ does not overrun the target memory area to avoid data corruption or protection faults.

address???

A valid 32-bit memory address, specifying the location in memory where the byte or sequence of bytes should be stored. Specifying an invalid address can result in a General Protection Fault (GPF).

datavalue

The data value to be stored at address???.

string_expr

A string constant, literal or string expression that specifies the sequence of bytes to be stored in memory starting at the byte referenced by address???.

Restrictions

If address??? (or any byte in the range covered by count&) references an invalid address (memory that is not allocated to the application), Windows will generate a General Protection Fault (GPF) and terminate the application. GPFs cannot be trapped with an ON ERROR error handler. Attempting to write into the "code segment" of an application will also generate a GPF, since the code segment is write-protected.

See also

, PEEK, STRPTR, VARPTR


PRINT statement

Purpose

Write data to the current console text page (screen) at the current location.

Syntax

PRINT [ExpList] [SPC(n)] [TAB(n)] [,] [;] [...]

Remarks

The PRINT statement has the following parts, which may occur in any order, and quantity within a single PRINT statement:

ExpList

and/or string expression(s) to be written to the console.

SPC(n)

An optional function used to insert n spaces into the printed output. Multiple use of the SPC argument is permitted in the PRINT statement, for example, between expressions. Values of n less than 1 are ignored.

TAB(n)

An optional function used to tab to the nth column before printing ExpList. Multiple use of the TAB argument is permitted in the PRINT, for example, to position arguments in columns. Values of n less than 1 are ignored.

{;|,}

Character that determines the position of the next character printed. A semicolon (;) means the next character is printed immediately after the last character; a comma (,) means the next character is printed at the start of the next print zone. Print zones begin every 14 columns.

If the final argument of a PRINT statement is a semicolon or comma, the caret position is maintained at the current location, rather than the default action of scrolling the print position to the start of the next line. For example:

PRINT "Hello";

PRINT " world!";

...produces the contiguous result "Hello world!"

If you omit all arguments, the PRINT statement prints a blank line. Printing always begins at the current caret position. Output of the PRINT statement cannot be redirected to a file or device, it always goes to the current active console screen. For redirectable output, use the STDOUT statement.

Any control codes such as Carriage Return, Line-Feed and Backspace are not interpreted by PRINT and will display on the screen as symbols in the current selected font.

A question mark may be used as an abbreviation for the PRINT statement.

PRINT USING$ is not one statement in PBCC as it was in DOS. USING$ is a seperate function, see the USING$ function for more information.

Restrictions

Printing a User-Defined Type (UDT) variable is allowed, but the UDT is treated as if is a fixed-length string. Therefore, if the UDT contains non-string data types or padding due to member alignment, the displayed byte-for-byte ASCII/ANSI representation of the bytes that comprise the non-string positions may appear as blocks of "garbage" characters.

It is not possible to directly PRINT a Variant, Object Variable, or an entire array (i.e., PRINT MyArr#()). For arrays, use a FOR/NEXT loop to iterate through the individual elements. In the case of Variants, they should first be translated to the intended data type with the VARIANT# or VARIANT$ function. For example:

DIM x AS VARIANT

x = 1

PRINT VARIANT#(x)

See also

CLS, COLOR, LINE INPUT, LOCATE, PAGE, PAGEACTIVE, PAGEVISIBLE, PCOPY, STDOUT, USING$

Example

PRINT TAB(10) "This is a test, this is only a test..."

PRINT a$; b$; SPC(2); d$; x&, y&;

? TAB(10) a$ "1" b$ "2",,,


PRINT# statement

Purpose

Write data to a device or sequential file.

Syntax

PRINT # fNum&, [ExpList] [SPC(n)] [TAB(n)] [,] [;] [...]

PRINT # fNum&, array$()

Remarks

The first form of the PRINT# statement has the following parts, which may occur in any order and quantity, within a single PRINT# statement:

fNum&

Number used in an OPEN statement to open a sequential file. It can be any numeric expression that evaluates to the number of an open file. Note that the Number symbol (#) preceding fNum& is not optional.

ExpList

and/or string expression(s) to be written to the file.

SPC(n)

An optional function used to insert n spaces into the printed output. Multiple use of the SPC argument is permitted in the PRINT statement, for example, between expressions. Values of n less than 1 are ignored.

TAB(n)

An optional function used to tab to the nth column before printing ExpList. Multiple use of the TAB argument is permitted in the PRINT, for example, to position arguments in columns. Values of n less than 1 are ignored.

{;|,}

Character that determines the position of the next character printed. A semicolon (;) means the next character is printed immediately after the last character; a comma (,) means the next character is printed at the start of the next print zone. Print zones begin every 14 columns.

If the final argument of a PRINT# statement is a semicolon or comma, PRINT# will not append the (default) CR/LF byte pair to the data as it is written to the file. For example:

PRINT #1, "Hello";

PRINT #1, " world!"

...produces the contiguous string "Hello world!" in the disk file.

If you omit all arguments, the PRINT# statement prints a blank line in the file (i.e., a CR/LF pair only), but you must include the comma after the file number. Because PRINT# writes an image of the data to the file, you must delimit the data so it is printed correctly. If you use commas as delimiters, PRINT# also writes the blanks between print fields to the file. Also, remember that spacing of data displayed on a text screen using monospaced characters may not work well when the data is redisplayed in a graphical environment using proportionally spaced characters.

If you are not careful, you can waste a lot of disk space with unnecessary spaces, or worse, put fields so close together that you cannot tell them apart when they are later input with INPUT#. For example:

PRINT #1,1,2,3

sends:

1 2 3

to file #1. Because of the 14-column print zones between characters, superfluous spaces are sent to the file. On the other hand:

PRINT #1,1;2;3

sends:

1 2 3

to the file, and you cannot read the separate numeric values from this record because INPUT# requires commas as delimiters. The best way to delimit fields is to put a comma between each field, like so:

PRINT #1, 1 "," 2 "," 3

which writes:

1, 2, 3

to the file, and wastes the least possible space and is easy to read with an INPUT# statement. The WRITE# statement delimits fields with commas automatically.

PRINT# is advantageous when writing a single or on each line in a file. Use PRINT# followed by a comma but no arguments to write a blank line (carriage return/linefeed) to a file:

PRINT #1, 'writes a blank line to file #1

array$()

When PRINT# specifies an array name with empty parentheses, the entire array is written to the disk file as text strings, with each element delimited by a CR/LF ($CRLF or CHR$(13,10)). Numeric arrays are converted to the ASCII text equivalent.

Restrictions

Arrays of User-Defined Types (UDTs) may not be used with the array form of the PRINT# statement.

See also

GET, GET$, INPUT#, LINE INPUT#, PUT, PUT$, WRITE#

Example

' Classic PRINT# statement example

SUB MakeFile

' opens a sequential file for output. Using PRINT #,

' it writes lines of different data types to the file.

x& = FREEFILE

OPEN "INPUT#.DTA" FOR OUTPUT AS #x&

StringVariable$ = "I'll be back."

IntegerVar% = 1000

FloatingPoint! = 30000.12

' Write a line of text to the sequential file.

PRINT #x&, StringVariable$

PRINT #x&, IntegerVar%

PRINT #x&, FloatingPoint!

CLOSE #x& ' close file variable

END SUB ' end procedure MakeFile

SUB ReadFile

' Opens a sequential file for input. Using INPUT #,

' reads lines of different types of data from the file.

x& = FREEFILE

OPEN "INPUT#.DTA" FOR INPUT AS #x&

RESET StringVariable$

RESET IntegerVar%

RESET FloatingPoint!

' Read a line of text from the sequential file.

INPUT #x&, StringVariable$

INPUT #x&, IntegerVar%

INPUT #x&, FloatingPoint!

CLOSE #x& ' close file variable

END SUB ' end procedure ReadFile

' Array mode PRINT# statement example

a$ = "Trevor, Bob, Bruce, Dave, Simon, Jenny"

DIM b$(1 TO PARSECOUNT(a$))

PARSE a$, b$()

ARRAY SORT b$()

OPEN "filename.txt" FOR OUTPUT AS #1

PRINT #1, b$()

CLOSE #1


PRINTER$ function

Purpose | Retrieve printer names and printer port names.
Syntax | device$ = PRINTER$([NAME | PORT], printernum&)
Remarks | printernum& specifies the printer number, from 1 to PRINTERCOUNT. If the NAME option is specified in the first position, the printer name is returned. If the PORT option is specified instead, the port name (e.g., LPT1) is returned.
See also | LPRINT ATTACH, PRINTERCOUNT, XPRINT ATTACH


PRINTERCOUNT function

Purpose | Retrieve the number of available (installed) printers.
Syntax | ncPrinters& = PRINTERCOUNT
See also | LPRINT ATTACH, PRINTER$, XPRINT ATTACH
Example | FUNCTION PBMAIN LOCAL ix AS LONG, sPrinters AS STRING FOR ix = 1 TO PRINTERCOUNT sPrinters = sPrinters & PRINTER$(NAME, ix) & $CRLF NEXT PRINT sPrinters END FUNCTION


PROFILE statement

Purpose

Capture a profile report detailing total execution times of the Subs and Functions in a program and write it to a disk file.

Syntax

PROFILE diskfilename$

Remarks

At the time the PROFILE statement is executed, a standard sequential file of the specified file name diskfilename$ is created. For the best results in executable files, the PROFILE statement should be the last statement executed in the PBMAIN/WINMAIN function.

The profile report contains a list of every Sub/Function within the same module (EXE or DLL), the number of times it was called, and the total elapsed time (in milliseconds) spent executing all instances of the procedure. These statistics appear in the disk file in that specific order on each line:

<Sub/Func Name>, <Call Count>, <Time mSec>

The profile report only describes Subs/Functions that physically reside within the module (EXE or DLL) where the PROFILE statement is located. Subs/Functions in an external EXE or DLL are not profiled individually; however, the time taken to call other Subs/Functions and DLL/API functions is included in the accumulated execution time of the calling Sub/Function.

It is highly recommended that you close all other applications when profiling a PowerBASIC application. When an application is being profiled, PowerBASIC must generate a considerable amount of extra code to gather all of the needed information. This extra code is generated whenever a valid PROFILE statement appears in your program, regardless of whether it is actually executed.

For final production code, use the #TOOLS OFF metastatement is used to ensure the highest performance levels.

Interpreting a profile report

The execution time of nested Subs/Functions needs to be understood in order to obtain a clear "picture" of the execution times. For example, consider the following results:

Sub/Func | calls | time
MySubA, | 1, | 11016
MySubB, | 100, | 10014

At first glance, these results may suggest a "bottleneck" in MySubA since it took MySubA 11016 milliseconds to execute just one call, whereas the average time for MySubB was only about 100 milliseconds per call (10014 mSec / 100 calls = 100.14 mSec).

However, if MySubB is actually called by MySubA, the results need to be assessed differently. For example, we could say: "MySubB took 10014 milliseconds of the 11016 milliseconds of the time spent in MySubA". Or to put it another way: "Of the 11016 milliseconds MySubA took to execute, 10014 milliseconds of that time was spent executing MySubB".

Interpolating these results, it can be easily calculated that the code in MySubA only took 1002 milliseconds to run, yet this blossomed to 11016 milliseconds because of its dependence on MySubB.

Therefore, improving the performance of MySubB would clearly improve the overall speed of MySubA, and the profile results of both functions would be improved accordingly.

Restrictions

Profiling is "enabled" when the first Sub/Function that contains a PROFILE statement begins execution. All Subs/Functions subsequently executed from within that Sub/Function are profiled.

It is not possible to profile the actual PBMAIN or WINMAIN functions. If a PROFILE statement occurs within PBMAIN/WINMAIN, all Subs/Functions that are called from PBMAIN/LIBMAIN are profiled normally.

Therefore, if PBMAIN/WINMAIN contains code that requires profiling, simply rename the function and create a new PBMAIN/WINMAIN function that immediately calls the renamed function and then executes a PROFILE statement. See the example below.

For application code with nested and lengthy Sub/Function calls, adding up the total number of milliseconds in the last column of a PROFILE disk file will usually produce a number that is far larger than the actual time it took your program to execute.

The time resolution of the profile report is limited by the Quantum supported by the operating system (Win95/98 is 54 mSec, and WinNT/2000/XP is 10 mSec), and can be influenced by any other applications which run concurrently. Nonetheless, PROFILE can offer a great insight as to which code may be consuming the most CPU time, and where optimization efforts should be concentrated.

See also

#TOOLS, CALLSTK, CALLSTK$, CALLSTKCOUNT, FUNCNAME$, TRACE

Example

SUB B1

SLEEP 1000

END SUB

SUB A1

SLEEP 250

CALL B1

END SUB

FUNCTION PBMAIN

CALL A1

PROFILE "Profile Results.txt" ' Profile at end

END FUNCTION

Result

A1, | 1, | 1252
B1, | 1, | 1002
PBMAIN, | 1, | 0


PROGID$ function

Purpose

Return the unique alphanumeric PROGID (text) associated with a unique CLSID string of a COM object or component. A COM object/component must include an alphanumeric PROGID string in order to be used by PowerBASIC (and Visual Basic).

Syntax

a$ = PROGID$(ClassID$)

Remarks

A PROGID string is the unique alphanumeric text name associated with a given COM object/component. For example, "Word.Application.8".

You convert the 16-byte (128-bit) binary class ID of a COM object/component into a PROGID string with the PROGID$ function.

PROGID$ takes the (16-byte) binary string ClassID$ representing the GUID or UUID of a COM object/component, and examines the system registry in order to determine the PROGID string associated with the ClassID$ string. ClassID$ may be a dynamic string or fixed-length string of at least 16 bytes, or (typically) a GUID variable.

If the ClassID$ cannot be found, or any error occurs in the lookup process, PROGID$ will not set the ERR system variable, but will return an empty string.

PROGID$ is the complement to the CLSID$ function. Using these two functions together, it is possible to extract the precise capitalization of the PROGID from the system registry. See the example below.

See also

DIM, CLSID$, GUID$, GUIDTXT$, INTERFACE/END INTERFACE, ISNOTHING, ISOBJECT, OBJECT, OBJACTIVE, OBJPTR, OBJRESULT, SET

Example

DIM MSWordClassID AS GUID

MSWordClassID = CLSID$("Word.Application")

IF TRIM$(MSWordClassID, $NUL) <> "" THEN

'Success getting the CLSID$ of MSWord

a$ = PROGID$(MSWordClassID)

'a$ now contains "Word.Application.8"

b$ = GUIDTXT$(MSWordClassID)

'b$ holds "{000209FF-0000-0000-C000-000000000046}"

END IF


PUT statement

Purpose

Write a record to a random-access file or a variable to a binary file.

Syntax

Random-Access and Binary files:

PUT [#] fNum&, [RecPos], [ABS] VarName

PUT [#] fNum& [, RecPos]

Binary files:

PUT [#] fNum&, [RecPos], Arr()

Remarks

The PUT statement has the following parts:

fNum&

A numeric literal or variable containing the file number used in the OPEN statement to open the file. The Number symbol (#) before fNum& is optional, but recommended for source code clarity.

RecPos

Identifies the position in the file to write the data. If RecPos is greater than the number of existing records or bytes in the file, the file is extended to the appropriate length, and the record is written at the specified position.

For random access files, RecPos is the record to be written, in the range 1 to (2^63)-1. If RecPos is omitted, the next record in sequence (following the one specified by the most recent GET, PUT or SEEK) is written. If the file was only just opened, the first record is written.

For binary files, RecPos is the starting byte position where VarName should be written. The default byte position is 1, unless the BASE = 0 clause was used in the OPEN statement. RecPos may be no larger than 2^63-1. RecPos is optional. If it is omitted, PowerBASIC uses the current file pointer position.

VarName

The name of a variable to write to the file. VarName can specify a simple variable, an element in an array, or a variable of User-Defined Type (UDT).

When writing a dynamic string to a random access file, PUT writes a 2-byte descriptor containing the string's length, before the actual string data. This descriptor reduces the available space in a record by two bytes. The descriptor is written with the low-order byte (record length MOD 256) occurring before the high-order byte (record length \ 256). If VarName contains more characters than record, VarName is truncated at record length less two bytes, and the descriptor is written to reflect the truncated string size.

When writing a dynamic string to a binary file, PUT only writes the actual string data: no length descriptor is written.

PUT is complementary to GET; it writes one record to a file. It is possible to PUT to records out of contiguous order, as in:

PUT #1, 1, MyVar

PUT #1, 100, MyVar

which creates a random-access file 100 records long. The data in records 2 through 99, however, are undefined until you explicitly PUT something there. PUT writes the contents of VarName to the specified record or byte positions.

(no VarName)

When the second form of PUT is used (without a VarName source string), PUT writes the data from an internal buffer into the file at the point where the file pointer indicates. This data must first be assigned to the file buffer using FIELD string variables.

ABS

When PUT is used to write a dynamic string to a random file, it normally precedes the actual data with a two-byte binary length Word to define the number of valid bytes in the record. If you precede the variable name with ABS (i.e., PUT #1, , ABS x$), no length Word is written: only the actual data, subject to the defined random record length. This offers greater compatibility with the actual operation of other versions of BASIC, such as PowerBASIC for DOS.

The record length in a random access file is limited to 32768 bytes, in order to ensure consistent behavior across all Win32 platforms.

Arr()

When PUT is used on a binary file, the entire array specified by Arr() is written to the file. With dynamic strings, the file is written in the PowerBASIC and/or VB packed string format. If the string is shorter than 65535 bytes, a 2-byte length Word is followed by the string data. Otherwise, a 2-byte value of 65535 is followed by a length Double-word (DWORD), then finally the string data.

With other data types, the entire data area is written as a single block.In either case, it is presumed the file will be read with the complementary GET Array statement.

See also

CSET, CSET$, FIELD, GET, LOF, LSET, PUT$, RSET, SETEOF, TYPE, WRITE#

Example

' Random-access PUT example

TYPE TestRec

uName AS STRING * 10

uNumber AS INTEGER

END TYPE

DIM Rec AS TestRec, Record AS QUAD

OPEN "RANDOM.DTA" FOR RANDOM AS #1 LEN = LEN(TestRec)

FOR Record = 1 TO 100

Rec.uName = "Joe" + STR$(Record)

Rec.uNumber = Record

PUT #1,Record, Rec

NEXT Record

CLOSE #1

' Binary PUT Array example

DIM TheData$(1 TO count&)

TheData$(1) = "text"

...

OPEN "Data file to write.dat" FOR BINARY AS #1

PUT #1, 1, TheData$()

CLOSE #1


PUT$ statement

Purpose

Write a to a file opened in binary mode.

Syntax

PUT$ [#] filenum&, string_expression

Remarks

PUT$ writes the contents of string_expression to file filenum& at the file's current file pointer position. File filenum& must have been opened in binary mode.

If the file pointer position is at the end of the file, PUT$ appends (adds) string_expression to the file, increasing its length by LEN(string_expression) bytes. If the file pointer is before the end of the file, PUT$ overwrites existing data with string_expression. In either case, the file pointer position following a PUT$ is at the end of the just-written string. You can use to retrieve or change the file pointer position.

See also

GET, GET$, OPEN, PUT, SEEK function, SEEK statement, SETEOF, WRITE#

Example

' Open a binary file and write the alphabet to it

OPEN "SEEK.DTA" FOR BINARY AS #1 BASE = 1

FOR I& = ASC("A") TO ASC("Z") ' 65 TO 90

PUT$ #1, CHR$(I&)

NEXT

CLOSE #1


RANDOMIZE statement

Purpose

Seed the random number generator.

Syntax

RANDOMIZE [number]

Remarks

number is a seed value that may be any type. If number is not specified, the value returned by the TIMER function is used.

Values returned by the random number generator (RND) depend on an initial seed value. For a given seed value, RND always returns the same sequence of values, yielding a predictable pseudo-random number sequence. Thus, any program that depends on RND will run exactly the same way each time unless a different seed is given.

The default seed can be duplicated with the following statement:

RANDOMIZE CVS(CHR$(255,255,255,255))

Note that each thread has its own, independent random number seed.

See also

RND, TIMER

Example

' Seed generator and get 5 random values

RANDOMIZE 1.5!

FOR I& = 1 TO 5

Table(I&) = RND(1,100)

NEXT I&

' Reseeding with the same starting value

' means you get the same sequence of values!

RANDOMIZE 1.5!

FOR I& = 1 TO 5

Table(I&) = RND(1,100)

NEXT I&

' Now reseed from the TIMER and we get

' a completely different set of values:

RANDOMIZE TIMER

FOR I& = 1 TO 5

Table(I&) = RND(1,100)

NEXT I&


READ$ function

Purpose

Retrieve data from a local DATA list.

Syntax

value$ = READ$(n%)

Remarks

The READ$ function is used to retrieve a specified string data item from a local DATA list, and returns the data in string format. READ$ offers a simple technique for random-access of local DATA.

n%

An expression, constant, or variable, which specifies an index position in the local DATA list. n% = 1 for the first data item, n% = 2 for the second, and so on. READ$ accesses the DATA statements in the order in which they appear in the source program, from left to right.

If n% is greater than DATACOUNT, READ$ returns an empty string, but no run-time error occurs.

value$

READ$ places the DATA string into value$.

If the target DATA statement is enclosed in quotes, READ$ preserves any leading or trailing spaces that it may contain; otherwise, READ$ leading and trailing spaces and returns a trimmed string. See DATA for more information on data item formatting.

If numeric data needs to be stored in DATA statements and retrieved with READ$, the VAL function can be used to convert the return values from READ$ into numeric values.

Restrictions

There is a limit of 64 Kilobytes and 16384 separate data items per Sub or Function, and it is not possible to read DATA from outside of the scope of current Sub or Function. Restrictions apply to using colon and underscore characters in DATA statements - see DATA for more information.

See also

DATA, DATACOUNT

Example

' The following returns the day of the week string.

FUNCTION WeekDayName$(BYVAL DayNum%)

IF DayNum% < 1 OR DayNum% > DATACOUNT THEN

WeekDayName$ = ""

ELSE

WeekDayName$ = READ$(DayNum%)

END IF

DATA Sun, Mon, Tue, Wed, Thu, Fri, Sat

END FUNCTION


REDIM statement

Purpose

Used at the to declare dynamic array variables and allocate, deallocate, or reallocate storage space.

Syntax

REDIM [PRESERVE] array[(subscripts)] [AS type] [AT address] [, ...]

Remarks

The REDIM statement allows dynamic arrays (including string arrays) to be erased and re-dimensioned. It is really just a shortcut for the two-step process ERASE x(), followed by DIM x(). REDIM uses the same basic syntax as the DIM statement.

array is the name of the array, and subscripts is either a group of single integers (one per dimension of a particular array), or a group of ranges (REDIM arr1(5 TO 25, 1 TO 4, 3 TO 8)), separated by commas.

Use the TO keyword instead of the colon (:) syntax, as the colon syntax may not be supported in future versions of PowerBASIC.

AS type

The AS type clause is optional, but recommended for the purposes of clarity.

AT address

The AT address clause indicates the array is to be an absolute array. Absolute arrays are not reset by the REDIM statement, nor are they reset when the Sub/Function exits, but they can be reset with the RESET statement. See the discussion in the DIM topic for more information on absolute arrays.

PRESERVE

The PRESERVE keyword tells the compiler to preserve the values of all existing elements in the array. For example, if you REDIM PRESERVE an array with 10 elements to 20 elements, the first 10 elements will retain their original value. The remaining 10 elements will be initialized to zero (or null/empty in the case of a string array). If the array is resized to be smaller, the specified number of elements is preserved, and the remaining elements are discarded. When PRESERVE is specified, you can resize only the upper boundary of the last (outer) dimension of the array. Arrays of only one dimension can always be resized.

In a Sub or Function, you can use REDIM to re-dimension an array that was passed as an argument. That is, when the complete array was passed to the Sub or Function:

CALL RemoveDuplicates( CustomerNames$() )

...

SUB RemoveDuplicates( a$() )

...

REDIM PRESERVE a$(1 TO NewCount&)

END SUB

REDIM may also be used to alter the size of Static and Global arrays.

When used with no subscript parameters, REDIM will erase all contents of an array and deallocate the memory used:

REDIM xyz&() ' Equivalent to ERASE xyz&()

Restrictions

REDIM cannot change the number of dimensions that an array has. When PRESERVE is specified, only the upper bound of the last (outer) dimension may be redefined.

When a REDIM statement is executed, the location of the array elements always moves in memory; however, the array's Descriptor location (VARPTR(arrayname()) will remain fixed at the original location. When using REDIM, your code must be sure to refresh any pointers that target the array data memory locations (STRPTR(arrayname(subscript)) for dynamic string arrays, and VARPTR(arrayname(subscript)) for all other array types).

While PowerBASIC supports lower boundary values that are non-zero, PowerBASIC generates the most efficient code if the lower boundary parameter is omitted (i.e., the array uses the default lower boundary of zero).

See also

ARRAYATTR, DIM, ERASE, RESET

Example

DIM MyData(40), Names$(100)

REDIM MyData(5 TO 50), Names$(10)


REGEXPR statement

Purpose

Scan a for a matching "wildcard" or regular expression.

Syntax

REGEXPR mask$ IN main$ [AT start&] TO iPos& [, iLen&]

Remarks

REGEXPR scans main$ for a matching expression specified in mask$. If found, it returns the position of the match in the iPos& variable (indexed to the first character position), and optionally, the length of the matching expression in iLen&.

If a match is made, the iPos& and iLen& results can be immediately used with subsequent string operations such as MID$ to extract the matched portion of main$, and/or to continue the search through the remainder of main$. If no matching expression is found, both iPos& (and iLen& if specified) are set to zero.

If specified, the search begins at the character position start& in main$; however, start& must be between 1 and the length of main$. If start& is less than 1, the start& parameter is ignored.

While it is possible for more than one match to be found in a particular target string, REGEXPR first selects one or more matches which start at the leftmost possible position, then returns the longest of those. Use the \s special escape operator to force a match on the shortest match (see below).

The ^ and $ operators match on both the actual string start/end, or the previous/next embedded line-delimiter characters (CHR$(13,10) or $CRLF) in main$. This enables REGEXPR to treat the main$ string as containing a set of "logical lines" of text. In this situation, the start& character position plays a crucial role in identifying which logical delimited line that should be examined by REGEXPR.

By default, search expressions are assumed to be case-insensitive, so capitalization is ignored.

mask$

The regular (wildcard) expression specified in mask$ may contain a combination of standard text characters and/or the metacharacters which are defined as follows:

char |  | Definition
. |  | (period) Matches any character, except the end-of-line.
^ |  | (caret) Matches the actual beginning-of-line position or the preceding line-delimiter character pair (CHR$(13,10) or $CRLF), as taken from the start& character position. The line-delimiter characters themselves are not included in the iLen& result. (also see [^] below for usage within a character class definition).
$ |  | (dollar) Matches the end-of-line position, which may be either the first line-delimiter character pair (CHR$(13,10) or $CRLF) that is encountered in the search to the right of the start& position, or the actual end of the main$ string, whichever occurs first. The line-delimiter characters themselves are not included in the iLen& result.
| |  | (stile) Specifies alternation (the OR operator), so that an expression on either side can match. Precedence is from left-to-right, as encountered in the expression.
? |  | (question mark) Specifies that zero or one match of the preceding sub-pattern is allowed. Cannot be used with a Tag.
+ |  | (plus) Specifies that one or more matches of the preceding sub-pattern are allowed. Cannot be used with a Tag.
* |  | (asterisk) Specifies that zero or more matches of the preceding sub-pattern are allowed. Cannot be used with a Tag.
Character classes
[ ] | (square brackets) Identifies a user-defined class of characters, any of which will match: [abc] will match a, b, or c. Only three special metacharacters are recognized within a class definition, the caret ^ for complemented characters, the hyphen - for a range of characters, or one of the following \ backslash escape sequences:
 | \\ \- \] \e \f \n \q \r \t \v \x##
 | Any other use of a backslash within a class definition yields an undefined operation that should be avoided.
[-] | (hyphen) The hyphen identifies a range of characters to match. For example, [a-f] will match a, b, c, d, e, or f.
 | Characters in an individual range must occur in the natural order as they appear in the character set. For example, [f-a] will match nothing.
 | Lists of characters, and one or more ranges of characters, may be intermixed in a single class definition. The start and end of a range may be specified by a literal character, or one of the \ backslash escape sequences:
 | \\ \- \] \e \f \n \q \r \t \v \x##
 | Any other use of a backslash within a class definition yields an undefined operation.
 | Multiple ranges in a class are valid. For example, [a-d2-5] matches a, b, c, d, 2, 3, 4, or 5.
 | When the hyphen is escaped, it is treated as a literal. For example, [a\-c] is a list, not a range, and matches a, -, or c due to the \ backslash escape sequence.
[^] | (caret) When the caret appears as the first item in a class definition, it identifies a complemented class of characters, which will not match. For example, [^abc] matches any character except a, b, or c.
 | A range can also be specified for the complemented class. For example, [^a-z] matches any character except a through z.
 | A caret located in any position other than the first is treated as a literal character.
Tags/sub-patterns
( ) | (parentheses) Parentheses are used to match a Tag, or sub-pattern, within the full search pattern, and remember the match. The matched sub-pattern can be retrieved later in the mask (or in a replace operation with REGREPL), with \01 through \99, based upon the left-to-right position of the opening parentheses.
 | Parentheses may also be used to force precedence of evaluation with the alternation operator. For example, "(Begin)|(End)File" would match either "BeginFile" or "EndFile", but without the Tag designations, "Begin|EndFile" would only match either "BeginndFile" or "BegiEndFile".
Escaped characters
\ | (backslash). The escape operator (single-character quote). The following character will be treated as a literal value rather than being interpreted as a special character. Note that the character following the backslash must actually be a special character, as follows:
\b | A word boundary. The start or end of a word, where a word is defined as one or more characters that include an alphabetic character (A-Z or a-z), a numeric character (0-9), and an underscore. For example, "abc_123" is considered a single word and "abc-123" is considered two words.
\c | Case-sensitive search. Without the \c operator, the default is to ignore case when matching. Unlike some other implementations of regular expressions, case-insensitivity is recognized in all operations, even a range of characters such as "[6-Z]". The \c operator may appear at any position in the mask.
\e | Escape character: CHR$(27) or $ESC.
\f | Formfeed character: CHR$(12) or $FF.
\n | Linefeed (or new-line) character: CHR$(10) or $LF.
\q | Double-quote mark ("): CHR$(34) or $DQ. \q is included for ease of inclusion within a literal string. For example: "\qHello\q".
\r | Carriage-return character: CHR$(13) or $CR.
\s | Shortest match character: The \s flag causes the shortest matching string to be returned, rather than the longest (the default). For example, when searching for the mask "abc.*abc" in "abcdabcabc", the default setting would return position 1 and length 10. With the \s switch set, it returns position 1 and length 7. This option may cause a slight increase in processing time. The \s flag must appear at the beginning of the mask string.
\t | Horizontal tab character: CHR$(9) or $TAB.
\v | Vertical tab character: CHR$(11) or $VT.
\x## | Hex character code: Indicates that an ASCII code follows, given by two hexadecimal digits. For example, \xFF = CHR$(&HFF) (which is equivalent to CHR$(255)). XX must be in the range 0 through 255.

Restrictions

To maximize performance, avoid overuse of the *, + and ? metacharacters.

See also

REGREPL

Example

a$ = "please send email to support@powerbasic.com"

b$ = "([a-z0-9._/+-]+)(@[a-z0-9.-]+)"

REGEXPR b$ IN a$ TO position&, length&

email_address$ = MID$(a$, position&, length&)

a$ = "Amount owed: $42.75 and is overdue!"

b$ = "\$[0-9.,]+"

REGEXPR b$ IN a$ TO position&, length&

amount$ = MID$(a$, position&, length&)

a$ = "Open 24 Hours"

b$ = "[^a-z ]+"

REGEXPR b$ IN a$ TO position&, length&

hours$ = MID$(a$, position&, length&)

a$ = "Line 1" + $CRLF + "Line 2" + $CRLF

b$ = "([0-9])$"

RESET position& : RESET length&

DO

position& = position& + length&

REGEXPR b$ IN a$ AT position& TO _

position&, length&

c$ = "Match at " + STR$(position&)

LOOP WHILE position&


REGISTER statement

Purpose

To define Register variables, which are local to a Sub or Function. The REGISTER statement provides an optimization hint to the compiler.

Syntax

REGISTER variable [AS type] [, variable [AS type]]

Remarks

The REGISTER statement is used to define certain local variables as Register variables - that is, variables which are stored directly in specific CPU registers, rather than in application memory. Since data in a CPU register can be accessed much faster, and with less code, Register variables are valuable optimization tools.

Register variables are always local to the Sub or Function where they appear. In the current version of PowerBASIC, there may be up to two integer-class variables (Word/Dword/Integer/Long) and up to four Extended-precision floats. It is possible that future versions of the compiler will change these limits, so you may declare an unlimited number of them. Any "extra" Register variables are automatically reclassified as locals during compilation.

The REGISTER statement allows you to choose which variables will be classified as Register variables. If you do not make the choice in a particular Sub/Function, the compiler will attempt to choose for you. By default, the compiler will always assign any integer-class local variables available. Extended-precision float variables will be automatically assigned only in Functions that contain no external Function calls.

Integer class Register variables are most efficient for variables that are updated or used often, such as For/Next loop counter variables, and variables that are used repeatedly as array indexes.

Floating-point Register variables should generally be chosen with a bit more caution, since the compiler must generate code to save and restore them to conventional memory around each call to a Sub or Function. In some rather rare cases, it is possible that floating-point Register variables could actually reduce execution speed. However, they are extremely valuable with intensive floating-point calculations in Functions that have few references to other Subs and Functions.

Due to the design of FPUs (floating point units), and the instruction sets available, the first float register variable declared in your program has far more optimization possibilities than the others do. Use care in choosing the variable which is used most within floating-point expressions (that is, on the right side of the '=' assignment operator), in order to gain the greatest advantage in execution speed. Also, remember it is typically valuable to assign floating-point constants to Register variables when they are used in repetitive or intensive calculations.

You must use care with Inline Assembler floating-point opcodes in Functions that enable Register variables. Floating-point Register variables may occupy up to four of the FPU registers, so you must limit your use of the x87 registers to the remaining four. Further, floating-point Register variables may never be referenced by name from Inline Assembler code, as the compiler cannot always track the register locations with absolute certainty.

Restrictions

VARPTR cannot be used on a Register variable.

PowerBASIC transparently prevents the automatic register conversion of the variable used in the TO clause of the DIALOG SHOW MODAL and DIALOG SHOW MODELESS statements. If the target variable is explicitly declared as a register variable, PowerBASIC raises a compile-time Error 491 ("Invalid register variable"). This is necessary as the result values stored in such variables may be assigned from the context of other Subs/Functions, and this may only occur with a memory variable.

See also

#REGISTER, Optimizing your code

Example

SUB ReindexDatabase() AS LONG

#REGISTER NONE ' I'll choose my own register vars.

REGISTER i AS LONG

REGISTER fVar AS EXT

...

END FUNCTION


REGREPL statement

Purpose

Scan a for a matching "wildcard" or regular expression, and replace it with a new value.

Syntax

REGREPL mask$ IN main$ WITH repl$ [AT start&] TO iPos&, newmain$

Remarks

REGREPL scans main$ for a matching regular expression specified in mask$. If a match is made, REGREPL replaces the matched text with the contents of repl$, and assigns the new text to newmain$. Additionally, REGREPL sets iPos& to reflect the character position immediately following the matched text in newmain$, so the operation can be repeated, if desired.

If no matching expression is found, iPos& will be set to zero, and newmain$ receives a direct copy of main$. In either case, main$ remains unchanged.

mask$ may contain literal characters and metacharacters (wildcards) to form the regular expression, and repl$ may only contain literal characters and tags specified by \##. Each tag from \01 through \99 is replaced by the text actually matched for that tag. \00 is replaced by the entire matched text.

If specified, the search begins at the character position start& in main$; however, start& must be between 1 and the length of main$. If start& is less than 1, the start& parameter is ignored.

While it is possible for more than one match to be found in a particular target string, REGREPL first selects one or more matches which start at the leftmost possible position, then returns the longest of those. Use the \s special escape operator to force a match on the shortest match (see below).

The ^ and $ operators match on both the actual string start/end, or the previous/next embedded line-delimiter characters (CHR$(13,10) or $CRLF) in main$. This enables REGREPL to treat the main$ string as containing a set of "logical lines" of text. In this situation, the start& character position plays a crucial role in identifying which logical delimited line that should be examined by REGREPL.

By default, search expressions are assumed to be case-insensitive, so capitalization is ignored.

mask$

The regular (wildcard) expression specified in mask$ may contain a combination of standard text characters and/or the metacharacters which are defined as follows:

char | Definition
. | (period) Matches any character, except the end-of-line.
^ | (caret) Matches the actual beginning-of-line position or the preceding line-delimiter character pair (CHR$(13,10) or $CRLF), as taken from the start& character position. The line-delimiter characters themselves are not replaced by repl$. (also see [^] below for usage within a character class definition).
$ | (dollar) Matches the end-of-line position, which may be the either the first line-delimiter character pair (CHR$(13,10) or $CRLF) that is encountered in the search to the right of the start& position, or the actual end of the main$ string, whichever occurs first. The line-delimiter characters themselves are not replaced by repl$.
| | (stile) Specifies alternation (the OR operator), so that an expression on either side can match. Precedence is from left-to-right, as encountered in the expression.
? | (question mark) Specifies that zero or one match of the preceding sub-pattern is allowed. Cannot be used with a Tag.
+ | (plus) Specifies that one or more matches of the preceding sub-pattern are allowed. Cannot be used with a Tag.
* | (asterisk) Specifies that zero or more matches of the preceding sub-pattern are allowed. Cannot be used with a Tag.
Character classes
[ ] | (square brackets) Identifies a user-defined class of characters, any of which will match: [abc] will match a, b, or c. Only three special metacharacters are recognized within a class definition, the caret (^) for complemented characters, the hyphen (-) for a range of characters, or one of the following \ backslash escape sequences:
 | \\ \- \] \e \f \n \q \r \t \v \x##
 | Any other use of a backslash within a class definition yields an undefined operation that should be avoided.
[-] | (hyphen) The hyphen identifies a range of characters to match. For example, [a-f] will match a, b, c, d, e, or f.
 | Characters in an individual range must occur in the natural order as they appear in the character set. For example, [f-a] will match nothing.
 | Lists of characters, and one or more ranges of characters, may be intermixed in a single class definition. The start and end of a range may be specified by a literal character, or one of the \ backslash escape sequences:
 | \\ \- \] \e \f \n \q \r \t \v \x##
 | Any other use of a backslash within a class definition yields an undefined operation.
 | Multiple ranges in a class are valid. For example, [a-d2-5] matches a, b, c, d, 2, 3, 4, or 5.
 | When the hyphen is escaped, it is treated as a literal. For example, [a\-c] is a list, not a range, and matches a, -, or c due to the \ backslash escape sequence.
[^] | (caret) When the caret appears as the first item in a class definition, it identifies a complemented class of characters, which will not match. For example, [^abc] matches any character except a, b, or c.
 | A range can also be specified for the complemented class. For example, [^a-z] matches any character except a through z.
 | A caret located in any position other than the first is treated as a literal character.
Tags/sub-patterns
( ) | (parentheses) Parentheses are used to match a Tag, or sub-pattern, within the full search pattern, and remember the match. The matched sub-pattern can be retrieved later in the mask, or in a replace operation, with \01 through \99, based upon the left-to-right position of the opening parentheses.
 | Parentheses may also be used to force precedence of evaluation with the alternation operator. For example, "(Begin)|(End)File" would match either "BeginFile" or "EndFile", but without the Tag designations, "Begin|EndFile" would only match either "BeginndFile" or "BegiEndFile".
 | Note: Parentheses may not be used with ? + * as any match repetition could cause the tag value to be ambiguous. To match repeated expressions, use parentheses followed by \01*.
Escaped characters
\ | (backslash). The escape operator (single-character quote). The following character will be treated as a literal value rather than being interpreted as a special character. Note that the character following the backslash must actually be a special character, as follows:
\b | A word boundary. The start or end of a word, where a word is defined as one or more characters that include an alphabetic character (A-Z or a-z), a numeric character (0-9), and an underscore. For example, "abc_123" is considered a single word and "abc-123" is considered two words.
\c | Case-sensitive search. Without the \c operator, the default is to ignore case when matching. Unlike some other implementations of regular expressions, case-insensitivity is recognized in all operations, even a range of characters such as "[6-Z]". The \c operator may appear at any position in the mask.
\e | Escape character: CHR$(27) or $ESC.
\f | Formfeed character: CHR$(12) or $FF.
\n | Linefeed (or newline) character: CHR$(10) or $LF.
\q | Double-quote mark ("): CHR$(34) or $DQ. \q is included for ease of inclusion within a literal string. For example: "\qHello\q".
\r | Carriage-return character: CHR$(13) or $CR.
\s | Shortest match character: The \s flag causes the shortest matching string to be returned, rather than the longest (the default). For example, when searching for the mask "abc.*abc" in "abcdabcabc", the default setting would return position 1 and length 10. With the \s switch set, it returns position 1 and length 7. This option may cause a slight increase in processing time. The \S flag must appear at the beginning of the mask string.
\t | Horizontal tab character: CHR$(9) or $TAB.
\v | Vertical tab character: CHR$(11) or $VT.
\x## | Hex character code: Indicates that an ASCII code follows, given by two hexadecimal digits. For example, \xFF = CHR$(&HFF) (which is equivalent to CHR$(255)). XX must be in the range 0 through 255.
\## | Tag number: Evaluated as the characters matched by tag number ## (where ## is in the range 01 through 99, in decimal). Tags are implicitly numbered from 01 through 99, based upon the left-to-right position of the left parenthesis. "(…)w\01" would match "abcwabc" or "456w456".

Tags cannot be forward-referenced - that is, if a reference is made to any Tag that is not yet defined, a non-match is presumed.

Restrictions

To maximize performance, avoid overuse of the *, + and ? metacharacters.

See also

REGEXPR

Example

#COMPILE EXE

FUNCTION PBMAIN

a$ = "please email support@powerbasic.com"

b$ = "([a-z0-9._/+-]+)(@[a-z0-9.-]+)"

c$ = "sales\02"

REGREPL b$ IN a$ WITH c$ TO position&, d$

' d$ -> "please email sales@powerbasic.com"

a$ = "Line 1" + $CRLF + "Line 2" + $CRLF

b$ = "([0-9])$"

c$ = "\01.0"

position& = 1

DO

REGREPL b$ IN a$ WITH c$ AT position& TO position&, a$

LOOP WHILE position&

' a$ -> " Line 1.0" + $CRLF + "Line 2.0" + $CRLF

END FUNCTION


REM statement

Purpose

Indicate that the remainder of a line in source code files is to be regarded as a Remark or Comment, and excluded from the compiled code.

Syntax

REM comment text

' comment text

; comment in an Inline Assembler statement

Remarks

The PowerBASIC compiler ignores Remarks; they do not take up space in your generated code, so use them abundantly - useful comments greatly increase the readability and maintainability of source code.

Comment text is any sequence of characters. A comment can appear on a line with other statements, but it must be the last thing on that line, and a colon must precede it. For example, the assignment below will not be compiled or executed because the compiler cannot tell where the comment ends and the statement begins:

REM now add the numbers: a = b + c

The following works:

a = b + c : REM now add the numbers

The apostrophe ( ' ) is an alternate form of REM. When you use an apostrophe, you do not need a colon to separate the remark from the other statements on the same line.

When using the Inline Assembler, use the semi-colon ( ; ) to indicate that the remainder of the line should be ignored. An apostrophe ( ' ) can still be used for comments, however.

In addition, the compiler treats text that appears after the line continuation character as a remark. However, we still recommend that such comments are preceded by a REM or an apostrophe ( ' ) symbol to clearly distinguish remarks from the actual code. For example:

DECLARE FUNCTION Call32& _ The prototype

LIB "CALL32.DLL" _ The DLL name

ALIAS "Call32" _ ' The exported name

(Param1 AS ANY, _ ' 1st parameter

BYVAL id&) ' 2nd parameter

For situations where a large section of code needs to be REMmed out (yet preserved within the source code file), it is often easier to enclose the code with #IF 0/#ENDIF metastatements. For example:

#IF 0 ' Exclude the following lines

Code and text in between the #IF 0 and #ENDIF

metastatements is ignored by the compiler.

DIM a$(1 TO 1000) ' This line is ignored too.

INCR x& ' As is this line!

#ENDIF

Since the #IF expression evaluates to false (zero), this forces the compiler to exclude the enclosed block of code from the compilation process, in exactly the same way as if a REM statement had been prefixed to each line.

See also

Long Lines

Example

x% = 10 : REM This is a comment

y% = 20 ' This is another form of comment

! MOV EAX,"ABCD" ; An Inline Assembler comment


REMAIN$ function

Purpose

Return the portion of a following the first occurrence of a specified character or string.

Syntax

a$ = REMAIN$([position&,] main$, [ANY] match$)

Remarks

REMAIN$ is a complement to the EXTRACT$ function. REMAIN$ uses the following parameters:

main$

The main$ string is searched for the character or string specified in match$. If found, all characters after match$ are returned in a$. If match$ is not found in main$ then an empty string is returned in a$.

position&

An optional starting position within main$. If not specified, the first position is used.

match$

The search string. If ANY is specified with match$, REMAIN$ will match the first instance of any single character in match$, not the whole string.

Restrictions

If position& evaluates to a position outside of the string on either side, or if position& is zero, an empty string is returned in a$.

See also

EXTRACT$, LEFT$, LTRIM$, MID$, REMOVE$, REPLACE, RIGHT$, RTRIM$, TALLY, TRIM$, VERIFY

Example

a$ = REMAIN$("I think, therefore I am hungry", ",")

Result " therefore I am hungry"


REMOVE$ function

Purpose

Return a copy of a with characters or strings removed.

Syntax

x$ = REMOVE$(MainString, [ANY] MatchString)

Remarks

The REMOVE$ function has the following parts:

MainString

The string expression from which to remove characters.

MatchString

The string expression to remove all occurrences of. If MatchString is not present in MainString, all of MainString is returned intact.

ANY

If the ANY keyword is included, MatchString specifies a list of single characters to be searched for individually, a match on any one of which will cause that character to be removed from the result.

Restrictions

REMOVE is case-sensitive.

See also

EXTRACT$, INSTR, LTRIM$, MID$, REPLACE, RETAIN$, RIGHT$, RTRIM$, TALLY, TRIM$, VERIFY

Example

' The following returns "aadabra",

' removing the string "bac"

x$ = REMOVE$("abacadabra", "bac")

' The following returns "dr",

' removing all "b", "a", and "c"

x$ = REMOVE$("abacadabra", ANY "bac")


REPEAT$ function

Purpose

Return a consisting of multiple copies of the specified string.

Syntax

s$ = REPEAT$(count&, string_expr)

Remarks

The REPEAT$ function has the following parts:

count&

Is an expression, constant or variable, specifying the number of copies of string_expr to be included in the result. REPEAT$ is very similar to STRING$ (which makes multiple copies of a single character).

string_expr

The string to be duplicated.

See also

CHR$, GUID$, NUL$, SPACE$, STRING$

Example

x$ = REPEAT$(5, "<*> ")

Result

<*> <*> <*> <*> <*>


REPLACE statement

Purpose

Within a specified string, replace all occurrences of one with another string.

Syntax

REPLACE [ANY] MatchString WITH NewString IN MainString

Remarks

The REPLACE statement replaces all occurrences of MatchString in MainString with NewString. The replacement can cause MainString to grow or shrink in size. MainString must be a string variable; MatchString and NewString may be string expressions. REPLACE is case-sensitive.

ANY

If you use the ANY option, within MainString, each occurrence of each character in MatchString will be replaced with the corresponding character in NewString. In this case, MatchString and NewString must be the same length, because there is a one-to-one correspondence between their characters.

See also

EXTRACT$, INSTR, LTRIM$, MID$, REMOVE$, RETAIN$, RIGHT$, RTRIM$, TALLY, TRIM$, VERIFY

Example

A$ = "abacadabra"

'now replace "bac" with "----bac----"

REPLACE "bac" WITH "----bac----" IN A$

A$ = "abacadabra"

'now replace all "b", "a", and "c" with "*"

REPLACE ANY "bac" WITH "***" IN A$


RESET statement

Purpose

Set a scalar (non-array) variable, Variant, User-Defined Type, individual array element (or an entire array) to zero or null/empty. RESET does not deallocate the actual memory used (with the exception of dynamic string array data, which is automatically deallocated).

Syntax

RESET variable [, ...]

RESET array() [, ...]

RESET array(index) [, ...]

Remarks

If variable is numeric, it is set to zero. If variable is a dynamic string, it is set to null (""; an empty string). If variable is an ASCIIZ string, the first byte is set to nul ($NUL). If variable is a fixed-length string or User-Defined Type/Union, all bytes in variable are set to nul, or CHR$(0). If variable is a Variant, it is cleared and set to data type %VT_EMPTY.

If array() is , all elements are set to zero; otherwise all elements are set to zero/null. If an array index value is specified within the parentheses, just that array element is set to zero/null, as if it were a scalar (non-array) variable.

RESET also works with absolute arrays, clearing the contents to zeroes or empty strings. For more information on absolute arrays, please refer to the DIM statement.

See also

ARRAYATTR, DIM, ERASE, REDIM, SET


RESUME statement

Purpose | Restart program execution after error handling with ON ERROR GOTO.
Syntax | RESUME [label | NEXT]
Remarks | The RESUME statement is used to continue execution of a program after a run-time error has been trapped and processed with an ON ERROR handler. If a label is specified, execution continues at the specified label. If NEXT is specified, execution continues on the line after the error occurred. If you do not specify a label or NEXT, execution continues where the error occurred, so make sure you’ve fixed the cause of the error first!
Restrictions | ON ERROR and RESUME may not be used within a TRY/END TRY block.
See also | ERL, ERR, ERROR, Error Overview, ERROR$, Error Trapping, ON ERROR, Run-time Errors
Example | See the examples in Error Trapping.


RETAIN$ function

Purpose | Return a string containing only the characters contained in a specified match string. All other characters are removed..
Syntax | sResult$ = RETAIN$(main$, [ANY] match$)
Remarks | RETAIN$ returns a string consisting of zero or more copies of the complete expression match$ which are found in main$. All other characters are removed.
ANY | If the ANY option is included, match$ specifies a list of single characters to be retained, if they are found in main$.
Restrictions | If match$ is an empty string, RETAIN$ returns an empty string.
See also | EXTRACT$, REMAIN$, REMOVE$, REPLACE
Example | a$ ="<p>1234567890<ak;lk;l>1234567890</p>" b$ = RETAIN$(a$, ANY "<;/p>") c$ = RETAIN$(a$, ANY "0123456789")
Result | b$ contains "<p><;;></p>" c$ contains " 12345678901234567890"


RETURN statement

Purpose

Return from a (GOSUB) subroutine to its caller.

Syntax

RETURN

Remarks

RETURN terminates the execution of a subroutine, and passes control to the statement directly following the calling GOSUB statement.

Performing a RETURN without a corresponding GOSUB can cause unexpected behavior and difficult-to-track errors, including the possibility of a General Protection Fault (GPF) in Windows.

See also

CALL, GOSUB, GOTO, ON ERROR, SUB/END SUB

Example

See the example in GOSUB.


RGB function

Purpose | Return an RGB color value for use with the Windows API palette and GDI functions.
Syntax | value& = RGB(red%, green%, blue%)
Remarks | red%, green% and blue% are values between 0 and 255, which represent the intensity of each color. If a color value is negative, it is assumed to be zero. If a color value is greater than 255 it is assumed to be 255. value& is the combined colors as a Long-integer value.
See also | BGR


RIGHT$ function

Purpose

Return the rightmost n characters of a .

Syntax

s$ = RIGHT$(string_expression, n&)

Remarks

If n& is positive, RIGHT$ returns the indicated number of characters from the string, starting from the right and working left. If n& greater than the length of string_expression, all of string_expression is returned.

If n& is 0, RIGHT$ returns an empty string. If n& is negative, it is interpreted as (LEN(string_expression) - ABS(n&)). For example, RIGHT$("1234567890", -2) returns "34567890".

See also

EXTRACT$, INSTR, LEFT$, LTRIM$, MID$, REMOVE$, REPLACE, RTRIM$, TALLY, TRIM$, VERIFY

Example

' Demonstrate LEFT$ and RIGHT$ functions

DIM aString$, x$, n AS LONG

aString$ = "ABCDEFGHIJKLMNOP"

FOR n = 1 TO 14 STEP 2

x$ = LEFT$(aString, n) + SPACE$(28 - n * 2) + RIGHT$(aString, n)

NEXT n


RMDIR statement

Purpose

Delete a disk directory (like the DOS RMDIR command).

Syntax

RMDIR path

Remarks

path is a directory path, which may include a drive specification. RMDIR deletes the directory indicated by path.

This statement works like the DOS "RMDIR" or "RD" commands. As with the DOS commands, the path must specify a valid, empty directory, other than the default (current) directory. Otherwise, a run-time Error 75 occurs ("Path/file access error").

RMDIR can use Long File Names (LFNs).

See also

CHDIR, KILL, MKDIR

Example

DirectoryName$ = "\TEMP"

RMDIR DirectoryName$


RND function

Purpose

Return a random number.

Syntax

y = RND

y = RND()

y = RND(a, b)

y = RND(numeric_expression)

Remarks

Floating point mode: RND and RND() return a random Extended-precision value that is less than 1, but greater than or equal to 0. Numbers generated by RND aren't really random, but are the result of applying a pseudo-random transformation algorithm to a starting ("seed") value. Given the same seed, PowerBASIC's RND algorithm always produces the same sequence of "random" numbers.

Integer Range mode: RND(a, b) returns a Long-integer in the range of a to b inclusive. a and b can each be a numeric literal or a numeric expression that evaluates within the full range of a Long-integer (-2,147,483,648 to 2,147,483,647). Additionally, a can be higher or lower in value than b.

Special effects mode: When used with a single numeric expression argument, the value returned by RND depends on the optional numeric value you supply as the argument, as follows:

With no argument, or with a positive argument, RND generates the next number in sequence based on the initial seed value. With an argument of 0, RND repeats the last number generated. A negative argument causes the random number generator to be re-seeded, so subsequent uses of RND with no argument or with a positive argument result in a new sequence of values.

Do not use 0 or negative value arguments in special effects mode unless you are looking for the special effects those argument values produce.

The random number generator can be reset back to the default seed using the following statement:

RANDOMIZE CVS(CHR$(255,255,255,255))

Note that each thread has its own, independent random number seed. See the discussion under RANDOMIZE for additional information on seeding the random number generator.

Example

See the example under RANDOMIZE.


ROTATE statement

Purpose

Rotate the bits in an variable.

Syntax

ROTATE {LEFT | RIGHT} ivar, count

Remarks

ivar must be one of the integer-class variable types: Byte, Word, Integer, Double-word, Long-integer, or Quad-integer. count is the number of bits by which to rotate ivar. ROTATE rotates all the bits in ivar without special regard to the sign bit of a signed integer-class variable.

See also

BIT function, BIT statement, BITS, SHIFT

Example

i? = 221 ' binary: 1 1 0 1 1 1 0 1

ROTATE RIGHT i?, 1 ' binary: 1 1 1 0 1 1 1 0


ROUND function

Purpose

Round a numeric value to a specified number of decimal places.

Syntax

x = ROUND(numeric_expression, n)

Remarks

n is an expression specifying the number of decimal places required in the result. ROUND is especially useful in cases where you have a variable in Single, Double, or Extended-precision, and you want to put it into a Currency variable or display it, rounded to a specific number of decimal places.

Rounding is done according to the "banker's rounding" principle: if the fractional digit being rounded off is exactly five, with no trailing digits, the number is rounded to the nearest even number. This provides better results, on average, than the simple "round up at five" approach.

A% = ROUND(0.5, 0) ' 0

A% = ROUND(1.5, 0) ' 2

A% = ROUND(2.5, 0) ' 2

A% = ROUND(2.51, 0) ' 3

See also

CEIL, FIX, FORMAT$, INT, USING$


RSET$ function

Purpose

Return a containing a right-justified (padded) string.

Syntax

a$ = RSET$(string_expression, strlen& [USING ustring_expression])

Remarks

RSET$ right-aligns the string string_expression into a string of strlen& characters.

USING

If ustring_expression is null (empty) or is not specified, RSET$ pads string_expression with space characters. Otherwise, RSET$ pads the string with the first character of ustring_expression.

If string_expression is shorter then strlen&, RSET$ right-justifies string_expression within the assigned string variable (a$), padding the left side as described above; otherwise, RSET$ returns the left-most strlen& bytes of string_expression.

See also

CSET, CSET$, GET, LET, LSET, LSET$, PUT, RESET, RSET, STRINSERT$, TYPE SET

Example

a$ = RSET$("PowerBASIC", 20)

' result: " PowerBASIC"

a$ = RSET$("PowerBASIC",20 USING "*")

' result: "**********PowerBASIC"


RSET statement

Purpose

Right justify a into the space of a string variable or User-Defined Type.

Syntax

RSET [ABS] result_var = string_expression [USING ustring_expression]

Remarks

RSET right-aligns a string within the space of another string, or within a variable of a User-Defined Type (UDT).

ABS

If ABS is specified, or ustring_expression is null (empty), RSET leaves the padding positions unchanged from their original content, rather than replacing them with spaces.

USING

If string_expression is shorter then result_var, RSET right-justifies string_expression within result_var, and pads remaining character positions on the left side using the first character in ustring_expression or spaces if not specified or is null (empty).

If string_expression is longer than result_var, RSET truncates string_expression from the right until it fits in result_var.

RSET can be used to assign the content of a User-Defined Type to a User-Defined Type variable of a different class, or assign a dynamic string to a User-Defined Type. For example:

RSET MyUDT = STRING$(LEN(MyUDT), 0)

RSET MyUDT = b$

LSET works in a similar manner, but left-aligns string_expression; CSET performs center-justification.

See also

CSET, CSET$, GET, LET, LSET, LSET$, PUT, RESET, RSET$, STRINSERT$, TYPE SET

Example

a$ = SPACE$(20)

RSET a$ = "Right-align"

' result " Right-align"

RSET a$ = "Right-align" USING "*"

' result "*********Right-align"


RTRIM$ function

Purpose

Return a copy of a with trailing characters or strings removed.

Syntax

x$ = RTRIM$(MainString [, [ANY] MatchString])

Remarks

MainString is the string expression from which to remove characters, and MatchString is the string expression specifying the characters that should be removed from the right hand side of MainString.

If MatchString is not specified, RTRIM$ removes trailing spaces. RTRIM$ returns a sub-string of MainString, from the beginning of the string to the character preceding the consecutive occurrences of MatchString (or space), which continues to the end of the original string. If MatchString (or a space) is not present at the end of MainString, all of MainString is returned.

RTRIM$ is case-sensitive.

ANY

If the ANY keyword is included, MatchString specifies a list of single characters to be searched for individually - a match on any one of which as a trailing character will cause the character to be removed from the result.

See also

EXTRACT$, INSTR, LEFT$, LTRIM$, MID$, REMOVE$, REPLACE, RIGHT$, STRDELETE$, STRINSERT$, STRREVERSE$, TALLY, TRIM$, VERIFY

Example

' returns "abacadabra" (match on spaces)

x$ = RTRIM$("abacadabra ")

' returns "abacadabra " (no match on " cad")

x$ = RTRIM$("abacadabra ", " cad")

' returns "abacadabr" (match on " " and "a")

x$ = RTRIM$("abacadabra ", ANY " cad")


SCREEN function

Purpose | Return the ASCII code of a character on the current console page (screen).
Syntax | c? = SCREEN(row%, column%)
Remarks | The result is a Byte value containing the ASCII code of the character at the specified location in the console window. row% has a valid range of 1 to SCREENY. column% has a valid range of 1 to SCREENX.
See also | ASC, SCREENATTR, SCREENX, SCREENY, SCROLL


SCREENATTR function

Purpose

Return a composite character attribute (color).

Syntax

c? = SCREENATTR(row%, column%)

Remarks

The character attribute is an value in the range of 0 through 255 (&hFF). The lowest 4-bits (low-order nibble) contains the foreground color attribute, in the range of 0-15, and the next 4-bits (high-order nibble) contains the background color attribute, in the range of 0-15.

If the coordinates are specified as 0,0, the default screen attribute (that which will be used for subsequent PRINT statements) is returned. Otherwise, the character attribute for the specified location is returned.

See also

COLOR, SCREEN, SCREENX, SCREENY, SCROLL

Example

Clr? = SCREENATTR(row&, col&)

ForeGround? = Clr? MOD 16

BackGround? = Clr? \ 16


SCREENX function

Purpose | Return the width of the console, in character columns. The console size may be changed using CONSOLE SCREEN.
Syntax | ncColumns% = SCREENX
Remarks | SCREENX and SCREENY return the size of the console screen buffer. That's because previous versions of PowerBASIC considered the visible size of the console window and the size of its memory buffer to be the same. PowerBASIC now allows you to create a virtual screen that may be larger than the visible screen. The two sizes are treated independently, although the virtual screen buffer may never be smaller than the visible window size. CONSOLE GET VIRTUAL now returns the size of the virtual screen buffer, and CONSOLE GET SCREEN returns the visible screen size. While the old syntax will still be supported for some period of time, we urge you to change to the new syntax as soon as possible, to avoid future compatibility issues.
See also | MOUSEX, MOUSEY, SCREEN, SCREENATTR, SCREENY, SCROLL


SCREENY function

Purpose | Return the height of the console, in character rows. The console size may be changed using CONSOLE SCREEN.
Syntax | ncRows% = SCREENY
Remarks | SCREENX and SCREENY return the size of the console screen buffer. That's because previous versions of PowerBASIC considered the visible size of the console window and the size of its memory buffer to be the same. PowerBASIC now allows you to create a virtual screen that may be larger than the visible screen. The two sizes are treated independently, although the virtual screen buffer may never be smaller than the visible window size. CONSOLE GET VIRTUAL now returns the size of the virtual screen buffer, and CONSOLE GET SCREEN returns the visible screen size. While the old syntax will still be supported for some period of time, we urge you to change to the new syntax as soon as possible, to avoid future compatibility issues.
See also | MOUSEX, MOUSEY, SCREEN, SCREENATTR, SCREENX, SCROLL


SCROLL statement

Purpose

Scroll all or part of the current console page.

Syntax

SCROLL [UP | DOWN | LEFT | RIGHT] [lines [, y1, x1[, y2, x2]]]

Remarks

y1 and x1 define the upper left corner of the region to scroll, while y2 and x2 define the lower right corner. If neither are specified, the entire page is scrolled.

lines specifies the number of rows/columns to scroll, with a default value of one.

If a direction (UP, DOWN, LEFT, RIGHT) is not specified, DOWN is presumed. The direction clause is considered to be the "source" of the scrolling.

For example, SCROLL DOWN means that a blank space is opened at the bottom of the scroll region, with remaining text moved up. SCROLL LEFT means that a blank space is opened at the left of the scroll region, with remaining text moved right.

See also

SCREEN, SCREENATTR, SCREENX, SCREENY

Example

' Open one line at the bottom, scroll the

' rest of the page up one line

SCROLL 1

' Leave the last row unchanged, open 1 row

' at the top, shift the remainder down.

SCROLL UP 1, 1, 1, SCREENY-1, SCREENX

' Leave the first row unchanged, open 1 row

' at the bottom, shift the remainder up.

SCROLL DOWN 1, 2, 1


SEEK function

Purpose

Return the location within a file where the next I/O operation will take place.

Syntax

position&& = SEEK([#] filenum&)

Remarks

If file filenum& was opened in random-access mode, SEEK returns the record number of the next record to be written or read as a Quad-integer (64-bit) value. If the file was opened in any other mode, SEEK returns the byte position of the next byte to be written or read, as a Quad-integer (64-bit) value. The Number symbol (#) is optional, but recommended for clarity.

The beginning byte position (for binary and sequential files) or record position (for random-access files) may be 0 or 1, depending on the BASE option used when the file was initially Opened. The default, if no BASE is specified, is a starting position of 1.

PowerBASIC recommends using the SEEK function over the (more complex) LOC function used in prior versions of PowerBASIC. LOC remains supported for compatibility with older versions of BASIC, but it is likely that LOC may be removed in future versions of PowerBASIC.

See also

EOF, FILEATTR, GET$, LOC, LOF, OPEN, PUT$, SEEK statement

Example

RANDOMIZE TIMER

OPEN "OUTPUT.TXT" FOR OUTPUT AS #1

PRINT #1, STRING$(RND * 80, RND * 255);

position&& = SEEK(1)

CLOSE #1


SEEK statement

Purpose

Set the position in a file for the next input or output operation.

Syntax

SEEK [#] filenum&, position&&

Remarks

SEEK sets the file pointer position of file filenum& to position&&. position&& is a Quad-integer variable, constant, or expression.

The next GET$ or PUT$ performed on the file filenum& will occur position&& bytes (or records) deep into the file. If file filenum& was opened in binary or sequential mode, position&& indicates the new file position in bytes; for random-access files, position is in records.

The first byte position (for binary and sequential files) or record position (for random-access files) may be 0 or 1, depending on the BASE option used when the file was initially Opened. If no BASE was specified, the default position is 1.

Use the SEEK function to determine a binary file's current pointer position, and LOF to determine its length. Seeking past the end of a file does not produce an error, but no data can be read from beyond the true end of the file.

See also

EOF, FILEATTR, GET$, LOC, LOF, OPEN, PUT$, SEEK function, SETEOF

Example

SUB CreateFile

' Open a binary file and writes 75 chars to it.

LOCAL I&

OPEN "SEEK.DTA" FOR BINARY AS #1

FOR I& = 48 TO 122

PUT$ 1, CHR$(I&)

NEXT I&

END SUB

FUNCTION ReadIt$(Start&&, qSize&&)

' SEEK to the correct position in the file,

' which was previously opened in the CreateFile SUB.

SEEK 1, Start&&

I&& = 1

TempStr$ = ""

' Read in the indicated data - don't read past end of file.

WHILE (ISFALSE EOF(1)) AND (I&& <= qSize&&)

GET$ 1, 1, Char$

TempStr$ = TempStr$ + Char$

INCR I&&

WEND

ReadIt$ = TempStr$ ' assign function's result

END FUNCTION


SELECT CASE/END SELECT block

Purpose

Control program flow based on the value of an expression.

Syntax

SELECT CASE [AS] [LONG | CONST | CONST$] expression

CASE [IS] testlist

{statements}

[CASE [IS] testlist

{statements}]

[CASE ELSE

{statements}]

END SELECT

Remarks

testlist is one or more tests, separated by commas, to be performed on expression. expression can be either or .

When a SELECT statement is encountered, expression is evaluated using the testlist in the first CASE clause. If the evaluation is FALSE, the evaluation is repeated using the next testlist. As soon as an evaluation is TRUE (non-zero), the statements following that CASE clause are executed, up to the next CASE clause.

Execution then passes to the statement following the END SELECT statement. If none of the evaluations is TRUE, the statements following the optional CASE ELSE clause are executed.

The tests that may be performed by a CASE clause include: equality, inequality, greater than, less than, and range ("from-to") testing. The SELECT CASE block can do string or numeric tests, but these cannot be interchanged.

Examples of numeric CASE clause tests include:

SELECT CASE numeric_expression

CASE > b ' relational; is expression > b?

CASE 14 ' equality (= is assumed); is expression equal to 14?

CASE b TO 99 ' range; is expression between the value of the

' variable b and 99 (inclusive)?

CASE 14, b ' two equality tests; is expression equal to

' 14 or equal to b?

CASE 25 TO 99,14 ' combination range and equality; is expression

' between 25 and 99 (inclusive) or equal to 14?

Examples of string CASE clause tests include:

SELECT CASE string_expression

CASE > b$ ' relational; is expression > b$?

CASE "X" ' equality (= is assumed); is expression equal

' to "X"?

CASE "A" TO "C" ' range; is expression between "A" and

' "C"(inclusive)?

CASE "Y", b$ ' two equality tests; is expression equal to

' "Y" or equal to b$?

CASE "A" TO "C","Q" ' combination range and equality; is expression

' between "A" and "C" (inclusive) or equal

' to "Q"?

When a CASE clause contains multiple tests separated by commas, a logical OR is performed. That is, if any one (or more) of the tests is TRUE, the entire clause is deemed to be TRUE.

Use EXIT SELECT to jump out of a SELECT block prematurely.

PowerBASIC now offers three optional modifiers to provide highly optimized code generation for specific circumstances. By default, numeric expressions are evaluated either as values (to offer the widest range of compatibility for any possible circumstance) or as (for example, if PowerBASIC can establish that all case clauses are integer class values, etc). Further, string expressions are evaluated dynamically to allow virtually any data. However, if limits on the type and range of the data used for CASE comparison are restricted, performance can be dramatically enhanced with the LONG, CONST or CONST$ clauses.

AS LONG

In this case, the controlling expression and the CASE expressions must evaluate in the range of a Long-integer. Each of these expressions are calculated dynamically, so all of the normal operators are still available. Performance is enhanced by the integer class of code generation, rather than floating-point. For example, DWORD values are treated as Long-integer values, so &H0FFFFFFFF??? and -1& would be considered equal values. This can help eliminate the need to use functions such as BITS??? when performing comparisons between signed and unsigned values.

AS CONST

In this case, the controlling expression must evaluate in the range of a Long-integer. However, each of the case values must be strictly specified by a numeric literal (or numeric equate) in the range of a Long-integer. Multiple case values may be given (CASE 2,3,7), but operators and ranges of values are not allowed. CASE ELSE is permitted. Performance is enhanced by the internal creation of a vector jump table, one entry for each number from the smallest to the largest case value.

While this form of the structure offers the utmost performance possible, the execution speed must be carefully weighed against the increased program size, particularly when using sparse case values. For example, with just two CASE values of 2 and 1000, the generated jump table would need 999 table entries (3996 bytes in size). The largest allowed jump table for this form is approximately 3200 entries (12K bytes). If exceeded, an Error 402 is generated ("Statement too long/complex").

AS CONST$

In this case, the controlling expression must evaluate to a dynamic (variable length) string of length zero through 255 bytes. However, each of the case values must be strictly specified by a string literal (a quoted string, or a string equate). Multiple case values may be given (CASE "a","Bob",$value), but operators and ranges of values are not allowed. Performance is enhanced by the internal creation of a vectored scan table, eight bytes for each case value specified.

See also

CHOOSE, CHOOSE&, CHOOSE$, EXIT SELECT, IF, IF block, IIF, IIF&, IIF$, MAX, MAX&, MAX$, MIN, MIN&, MIN$, ON GOTO, ON GOSUB, SWITCH, SWITCH&, SWITCH$

Example

DIM Dwrd AS DWORD

DIM Lint AS LONG

Dwrd = &H0FFFFFFFF???

Lint = -1&

SELECT CASE Lint

CASE Dwrd

a$ = "A Match!"

CASE ELSE

a$ = "*No Match"

END SELECT

SELECT CASE AS LONG Lint

CASE Dwrd

a$ = "*A Match!"

CASE ELSE

a$ = "No Match"

END SELECT

SELECT CASE AS CONST Dwrd

CASE -1&

a$ = "*A Match!"

CASE 0

a$ = "No Match"

END SELECT

Result

*No Match

*A Match!

*A Match!


SET statement

Purpose

Assign a Dispatch Interface reference to an object variable in preparation to use a COM interface.

SET has been superceded by the LET statement, although SET remains supported for a limited period. Existing code should be converted to the new syntax as soon as possible. Simply remove the keyword SET from your source code.

Syntax

SET objvar = [NEW] [DISPATCH | interfacename] IN ProgramID$

SET objvar = objvar

SET vrntvar = objvar

SET objvar = vrntvar

SET objvar = NOTHING

Remarks

The five forms of the SET statement operate as follows:

SET objvar = [NEW] [DISPATCH | interfacename] IN ProgramID$

Creates a reference to a Dispatch Interface of a named COM object, using either early-binding or late-binding.

NEW

If the NEW keyword is used, SET creates a new instance of the COM server object and assigns the reference to the object variable objvar.

Without the NEW keyword, SET creates an Interface to an existing instance (initialized and running) COM server application, and assigns the reference to the object variable objvar.

DISPATCH

If the DISPATCH keyword is used, the reference is late-bound to the default or primary Dispatch Interface of the object. Object variable objvar must be a DISPATCH variable. Late-binding is less efficient than early-binding.

interfacename

If the Interface name is provided, the reference will be early-bound to the named Dispatch Interface of the object. objvar must be dimensioned as an object variable of that Interface name (DIM objvar AS interfacename). Early-binding requires a declaration of the Interface with an INTERFACE/END INTERFACE block.

ProgramID$

ProgramID$ specifies the registered object name, for example, "Word.Application.8". ProgramID$ is not case-sensitive and may be a string variable, string literal or string equate.

In all cases, once the object reference has been established in objvar, the object's Interface can be accessed using the OBJECT statement; however, if the Interface cannot be opened (for example, if the Dispatch Interface is not supported in ProgramID$), the object variable objvar is set to NOTHING. You can test for success or failure with the ISOBJECT or ISNOTHING functions.

SET objvar1 = objvar2

Provided both variables have been dimensioned as DISPATCH object variables, an object copy is created. Success or failure of the operation can be confirmed with the ISOBJECT or ISNOTHING functions.

SET vrntvar = objvar2

Attempts to open an IDispatch Interface, else an IUnknown Interface, on the object of objvar. If successful, SET assigns the reference to the Variant variable vrntvar. Variant variables cannot contain references to custom interfaces, only IDispatch or IUnknown Interfaces.

If the assignment is successful, VARIANTVT(vrntvar) will return either %VT_UNKNOWN or %VT_DISPATCH. If it is unsuccessful, vrntvar is set to %VT_EMPTY.

SET objvar = vrntvar

Opens an interface of the appropriate class for objvar on the object of vrntvar, and assigns that Interface reference to objvar. vrntvar must contain a reference to an object of type %VT_UNKNOWN or %VT_DISPATCH or the operation is ignored.

If the desired interface cannot be opened, the object variable objvar is set to NOTHING. Success or failure of the operation can be confirmed with the ISOBJECT or ISNOTHING functions.

SET objvar = NOTHING

Destroys an object variable. Once all variables that reference an object are destroyed, the object itself is destroyed automatically.

Restrictions

Run-time errors during the execution of the SET statement are not reflected in the ERR system variable. Rather, success or failure of the SET statement can be tested with the ISOBJECT or ISNOTHING functions. The VARIANTVT function can be used to test the content of a Variant variable. Other operations on variant variables can be performed with the LET statement.

See also

DIM, CLSID$, GUID$, GUIDTXT$, INTERFACE/END INTERFACE, ISNOTHING, ISOBJECT, LET, OBJECT, OBJACTIVE, OBJPTR, OBJRESULT, PROGID$, RESET

Example

' Reference an existing instance of MSWORD. If there is no existing instance,

' we try to create a new instance and reference that.

TRY

DIM oWordApp AS WordApplication

SET oWordApp = WordApplication IN "Word.Application"

IF ISFALSE ISOBJECT(oWordApp) THEN ERROR 5

CATCH

SET oWordApp = NEW WordApplication IN "Word.Application"

IF ISFALSE ISOBJECT(oWordApp) THEN

' MSWORD COM Server not installed?

EXIT FUNCTION

END IF

END TRY

' Obtain an interface from an interface member method,

' and assign it to a new object variable

DIM oWordDoc AS WordDocument

DIM vVnt AS VARIANT

ERRCLEAR

vVnt = 0

OBJECT CALL oWordApp.Documents.Add TO vVnt

IF OBJRESULT OR ERR THEN EXIT FUNCTION ' failed

SET oWordDoc = vVnt ' Assign the interface


SETATTR statement

Purpose

Set the file system attribute(s) of a disk file or directory.

Syntax

SETATTR filespec$, attribute

Remarks

filespec$ specifies a filename (optionally including a drive letter and directory path). attribute is a standard operating system attribute code:

Attribute | Description | Equate*
0 | Normal | %NORMAL
1 | Read-only | %READONLY
2 | Hidden | %HIDDEN
4 | System | %SYSTEM
32 | Archived | %ARCHIVE
* These equates can be found in the WIN32API.INC file

The attribute code of a given file or directory may be constructed from a combination of individual attribute values. For example, if you use an attribute of 0, filespec$ will be a regular file: not read-only, not hidden, not system, and not archived.

See also

DIR$, FILEATTR, GETATTR

Example

Files$ = "MYTEST.DAT"

SETATTR Files$, %HIDDEN + %SYSTEM

IF ISFALSE ERR THEN a$ = Files$ + " has been hidden!"


SETEOF statement

Purpose

Truncate or extend an open file to its current file pointer (read/write) position.

Syntax

SETEOF [#] filenum&

Remarks

SETEOF will truncate or extend an open file to its current file pointer (read/write) position, which may be set explicitly with the SEEK statement.

Unlike 16-bit Windows and DOS BASIC, Win32 will not truncate a file if you simply write an empty string to it, so the SETEOF statement is provided to cater for this need.

See also

CLOSE, FILEATTR, FLUSH, OPEN, SEEK function, SEEK statement

Example

FUNCTION PBMAIN

OPEN "Temp.dat" FOR BINARY AS #1 BASE = 1

A$ = SPACE$(50)

PUT$ #1, A$

' File is now 50 bytes

SEEK #1, 15

' Move to the 15th byte and truncate there

SETEOF #1

' File is now 14 bytes

CLOSE #1

END FUNCTION


SGN function

Purpose

Return the sign of a numeric expression.

Syntax

y = SGN(numeric_expression)

Remarks

If numeric_expression is positive, SGN returns 1. If numeric_expression is zero, SGN returns 0. If numeric_expression is negative, SGN returns -1.

In conjunction with the ON GOTO and ON GOSUB statements, SGN can produce a FORTRAN-like three-way branch:

ON SGN(balance) + 2 GOTO InTheRed, BreakingEven, InTheMoney

See also

ABS, IF, ON GOSUB, ON GOTO, SELECT

Example

' ON SGN value, GOSUB appropriate subroutine

ON SGN (value) + 2 GOSUB Minus, Zero, Plus

...

Minus:

x$ = "The product is negative" : RETURN

Zero:

x$ = "The product is zero" : RETURN

Plus:

x$ = "The product is positive" : RETURN


SHELL function

Purpose

Run an executable program asynchronously (as a separate process), while execution of the original application continues uninterrupted.

Syntax

ProcessId??? = SHELL([HANDLES,] CmdString [, WndStyle])

Remarks

The SHELL function has the following parts:

CmdString

The name of the program to execute ("child process"), along with and any required arguments or command-line switches.

WndStyle

A number corresponding to the style of the window in which the child process is to be executed. If WndStyle is omitted, the program is opened normal with focus, the same as WndStyle = 1.

The following table identifies the values for WndStyle and the resulting style of window:

WndStyle | Window style
0 | Hide window
1 | Normal with focus (default)
2 | Minimized with focus
3 | Maximized with focus
4 | Normal without focus
6 | Minimized without focus

SHELL returns the process id of the child process. The process id is a 32-bit LONG or DWORD value that identifies the child process, if it’s a 32-bit process. If the process id is zero, the child process is not a 32-bit process, or an error occurred. Use ERR to detect the success of the SHELL function. The HANDLES option allows the child process to inherit the file handles opened by your program. This affects only Windows handles, not PowerBASIC file identifiers. It is an advanced option, for those who know it works and why they need it.

Restrictions

Child processes run asynchronously, or independently of the program that SHELLs. So, the child process is, probably, still running after control returns from SHELL to your program. Also, if your program ends before the child process, the child process will continue to run.

To use internal DOS commands like DIR and COPY, you must run the DOS command processor, passing the DOS command as a parameter. See the example below.

If the program name in CmdString does not include an explicit path, Windows will search for the file in the following paths: the directory where the current program is located, the default directory, the 32-bit Windows system directory, the 16-bit Windows system directory, the Windows directory, and any directories listed in the PATH environment variable.

See also

ERR, SHELL statement

Example

pid??? = SHELL(MyApp$,1)

pid??? = SHELL(ENVIRON$("COMSPEC") + " /C DIR *.* > filename.txt")


SHELL statement

Purpose

Run an executable program synchronously. The SHELLing thread of the calling program is suspended until the SHELLed program ends.

Syntax

SHELL [HANDLES,] CmdString [, WndStyle]

Remarks

The SHELL statement has the following parts:

CmdString

The name of the program to execute ("child process"), along with and any required arguments or command-line switches.

WndStyle

A number corresponding to the style of the window in which the program is to be executed. If WndStyle is omitted, the program is opened normal with focus, the same as WndStyle = 1.

The following table identifies the values for WndStyle and the resulting style of window:

WndStyle | Window style
0 | Hide window
1 | Normal with focus (default)
2 | Minimized with focus
3 | Maximized with focus
4 | Normal without focus
6 | Minimized without focus

Use ERR to detect the success of the SHELL function. The HANDLES option allows the child process to inherit the file handles opened by your program. This affects only Windows handles, not PowerBASIC file identifiers. It is an advanced option, for those who know it works and why they need it.

Restrictions

The SHELL statement the child process synchronously. That is, SHELL will not return control to your program until the child program finishes.

To use internal DOS commands like DIR and COPY, you must run the DOS command processor, passing the DOS command as a parameter. See the example below.

If the program name in CmdString does not include an explicit path, Windows will search for the file in the following paths: the directory where the current program is located, the default directory, the 32-bit Windows system directory, the 16-bit Windows system directory, the Windows directory, and any directories listed in the PATH environment variable.

See also

ERR, SHELL function

Example

SHELL MyApp$,1

SHELL ENVIRON$("COMSPEC") + " /C DIR *.* > filename.txt"


SHIFT statement

Purpose

Shift the bits in an variable.

Syntax

SHIFT [SIGNED] {LEFT | RIGHT} ivar, countexpr

Remarks

ivar must be one of the integer-class variable types: Byte, Word, Integer, Double-word, Long-integer, or Quad-integer. countexpr is an integer-class expression specifying the number of bits by which to shift ivar.

SHIFT shifts all the bits in ivar without special regard to the sign bit of a signed integer-class variable.

SIGNED

The SIGNED option shifts everything, but does not allow the sign (positive or negative) of the value to change.

LEFT | RIGHT

The LEFT or RIGHT option determines the direction of the bit SHIFT operation. SHIFT LEFT shifts the bits toward the high-order end of ivar, and SHIFT RIGHT shifts bits toward the low-order end of ivar.

See also

AND, BIT function, BIT statement, BITS functions, NOT, OR, ROTATE, XOR

Example

DIM i AS BYTE

n = 221

SHIFT LEFT n, 1 ' binary 1 1 0 1 1 1 0 1

' n = 186 ' binary 1 0 1 1 1 0 1 0

n = 221

SHIFT RIGHT n, 1 ' binary 1 1 0 1 1 1 0 1

' n = 110 ' binary 0 1 1 0 1 1 1 0

n = 221

SHIFT SIGNED RIGHT n, 1 ' binary 1 1 0 1 1 1 0 1

n = 238 ' binary 1 1 1 0 1 1 1 0


SIN function

Purpose

Return the sine of its argument.

Syntax

y = SIN(numeric_expression)

Remarks

numeric_expression is an angle specified in radians. SIN returns an Extended-precision value between -1 and +1.

To convert radians to degrees, multiply by 57.29577951308232##. To convert degrees to radians, multiply by 0.0174532925199433##. For more information on radians, see the ATN function.

The Inverse Sine (ARCSIN) of a value can be calculated as follows:

ArcSin = ATN(Value / SQR(1 - Value * Value))

The Hyperbolic Sine (SINH) of a value can also be calculated:

SinH = (EXP(Value) - EXP(-Value)) / 2

The Inverse Hyperbolic Sine (ARCSINH) of a value can also be calculated:

ArcSinH = LOG(Value + SQR(Value * Value + 1))

' Useful Macro functions

MACRO Pi = 3.141592653589793##

MACRO DegreesToRadians(dpDegrees) = (dpDegrees * 0.0174532925199433##)

MACRO RadiansToDegrees(dpRadians) = (dpRadians * 57.29577951308232##)

See also

ATN, COS, TAN

Example

pi## = 3.141592653589793##

FOR I& = 0 TO 360 STEP 45

x$ = "The Sine of " + FORMAT$(I&,"* 0") + _

" degrees =" + FORMAT$(SIN(pi## / 180 * _

I&),"* 0.00")

NEXT I&

Result

The Sine of 0 degrees = 0.00

The Sine of 45 degrees = 0.71

The Sine of 90 degrees = 1.00

The Sine of 135 degrees = 0.71

The Sine of 180 degrees = 0.00

The Sine of 225 degrees = -0.71

The Sine of 270 degrees = -1.00

The Sine of 315 degrees = -0.71

The Sine of 360 degrees = 0.00


SIZEOF function

Purpose

Return the total or physical length of any PowerBASIC variable.

Syntax

x& = SIZEOF(target)

Remarks

Particularly useful for determining the maximum length of a fixed-length string, ASCIIZ string, or User-Defined Type. It provides similar functionality to LEN, which returns the current length of a data item.

target can be the name of any variable type (fixed-length string, ASCIIZ string, User-Defined Type (UDT) variable or definition, etc).

When measuring the size of a padded (aligned) UDT variable or definition with the SIZEOF (or LEN) statement, the measured length includes any padding that was added to the structure. For example, the following UDT structure:

TYPE LengthTestType DWORD

a AS INTEGER

END TYPE

...

DIM abc AS LengthTestType

x& = SIZEOF(abc) ' or use SIZEOF(LengthTestType)

Returns a length of 4 bytes in x&, since the UDT was padded with 2 additional bytes to enforce DWORD alignment. Note that the SIZEOF of individual UDT members returns the true size of the member without regard to padding or alignment. In the previous example, SIZEOF(abc.a) returns 2.

When used on a dynamic (variable length) string, SIZEOF returns 4, which is the size of the string handle. To obtain the length of the string data in the dynamic string, use the LEN function. SIZEOF also returns 4 for variables, since a pointer is always stored as a DWORD.

Pointers

When used with a dereferenced pointer (i.e., SIZEOF(@p), SIZEOF returns the size of the pointer target variable type, as defined in the DIM x AS y PTR [* pSize] statement.

For example, with a dynamic string pointer, SIZEOF returns 4. If the pointer target is a fixed-length string, UDT, Union, or ASCIIZ string, SIZEOF returns the size of the target data structure. However, if the pointer is declared to reference an ASCIIZ with no specific target size (i.e., DIM a AS ASCIIZ PTR), SIZEOF returns 0.

Likewise, if SIZEOF is used on a ASCIIZ string that does not have an explicit length specification, SIZEOF will also return 0. For example:

SUB ProcessData(BYREF szText AS ASCIIZ)

' Within this Sub, SIZEOF(szText) will return 0 because there is no explicit length specification

See also

DIM, LEN

Example

DIM Strval AS ASCIIZ * 10

Strval = "test"

' SIZEOF(Strval) = 10, LEN(Strval) = 4

DIM Intval AS QUAD

Intval = 1

' SIZEOF(Intval) = 8, LEN(Strval) = 8

DIM CustName AS STRING

CustName = "Fred Dagg"

' SIZEOF(CustName) = 4, LEN(CustName) = 9

UNION Arrs

m1(1 TO 1024) AS BYTE

END UNION

DIM p1 AS STRING PTR

DIM p2 AS STRING PTR * 1024

DIM p3 AS Arrs PTR

DIM p4 AS ASCIIZ PTR

DIM p5 AS ASCIIZ PTR * 64

' Results of SIZEOF on these pointers:

' SIZEOF(p1) = 4, SIZEOF(@p1) = 4

' SIZEOF(p2) = 4, SIZEOF(@p2) = 1024

' SIZEOF(p3) = 4, SIZEOF(@p3) = 1024

' SIZEOF(p4) = 4, SIZEOF(@p4) = 0

' SIZEOF(p5) = 4, SIZEOF(@p5) = 64

' SIZEOF(Arrs) = 1024


SLEEP statement

Purpose

Pause the current thread of the application for a specified number of milliseconds (mSec), allowing other processes (or threads) to continue.

Syntax

SLEEP m&

Remarks

m& is the number of milliseconds (1 millisecond = 1/1000th of a second) to pause the application. Only the current pauses. If other threads are present, they will continue to execute. During the SLEEP period, all time-slices for the current thread are given to other threads and processes.

If m& is zero, the remainder of the current time-slice is relinquished. If there are no other threads of equal priority, execution continues immediately.

The time-slice duration (also known as the Quantum) can vary from version to version of Windows, ranging from 20 mSec to 120 mSec. Therefore, the Quantum can affect the performance of applications when SLEEP 0 is overused. That is, excessive use of SLEEP 0 can cause an application to cede much of its available processor time, causing a significant drop in application performance.

When code is running in a tight loop, it is quite possible to use up 100% of the available CPU time, so the occasional use of SLEEP 0 within a tight loop is often beneficial to overall performance of the target PC. For example, it may not be necessary to use SLEEP 0 for every iteration of a loop, but every second or third instead.

See also

THREAD CREATE, TIMER

Example

' Pause for 5 seconds

SLEEP 5000

' Release time-slice every 256 iterations

FOR x& = 0 TO &H0FFFFFFFF&

' code goes here

IF x& MOD 256 = 0 THEN SLEEP 0

NEXT x&


SPACE$ function

Purpose

Return a consisting of a specified number of spaces.

Syntax

s$ = SPACE$(numeric_expression)

Remarks

numeric_expression is a non-negative expression that specifies how many spaces the function is to return. SPACE$ can be useful for formatting or prefilling strings.

See also

CHR$, CSET, CSET$, LSET, NUL$, REPEAT$, RSET, STRING$

Example

A$ = SPACE$(1000000) ' fill A$ with 1000000 spaces


SQR function

Purpose

Return the square root of its argument.

Syntax

y = SQR(numeric_expression)

Remarks

numeric_expression must be greater than or equal to zero. SQR calculates square roots using an optimized algorithm. That is, y = SQR(x) takes less time to execute than y = x^0.5.

Attempting to take the square root of a negative number does not produce any run-time errors, but the results of such an operation are undefined.

SQR returns an Extended-precision result.

See also

EXP, EXP2, EXP10, LOG, LOG2, LOG10


STATIC statement

Purpose

Declare static variables inside a Sub or Function. Static variables retain their values as long as the program is running.

Syntax

STATIC variable[()] [AS type] [, variable[()]]

STATIC variable[()] [, variable[()]] [, ...] AS type

Remarks

The STATIC statement is valid only inside a Sub or Function. Static variables retain their values even after the Sub or Function ends. A static variable is local to its Sub or Function, and can have the same name as other variables in other parts of the program without conflict.

To declare an array as a static variable, use an empty set of parentheses in the variable list: You can then use the DIM statement to dimension the array.

STATIC MyArray%()

STATIC StringArray() AS STRING

The STATIC statement may, optionally, accept a list of variables, all of which are defined by the type descriptor keyword that follows them. For example:

STATIC aaa, bbb, ccc AS INTEGER

STATIC vptr, aptr() AS LONG PTR

Restrictions

DEFtype has no effect on variables defined by a STATIC statement.

See also

DIM, GLOBAL, LOCAL, THREADED

Example

#COMPILE EXE

#DIM ALL

#INCLUDE "WIN32API.INC"

DECLARE SUB DoMessage ()

FUNCTION PBMAIN

DIM z%

FOR z% = 1 TO 5

DoMessage

NEXT z%

END FUNCTION

SUB DoMessage ()

STATIC x AS INTEGER

STATIC Message() AS ASCIIZ * 256

DIM Message(1 TO 5) AS STATIC ASCIIZ * 256

INCR x 'add one to x

Message(x) = "X =" + STR$(x)

#IF %DEF(%PB_CC32)

PRINT Message(x)

#ELSE

MSGBOX Message(x)

#ENDIF

END SUB


STDEOF function

Purpose | Return TRUE (non-zero) if the end of standard input has been reached.
Syntax | x& = STDEOF
Remarks | Return TRUE (non-zero) if the next reference to STDIN LINE will attempt to read past the logical end-of-file (CTRL+Z for keyboard input), or FALSE (zero) otherwise.
See also | CONSHNDL, CONSIN, CONSOUT, GETSTDERR, GETSTDIN, GETSTDOUT, STDERR, STDIN LINE, STDOUT


STDERR statement

Purpose | Output a string expression to the standard error device (console/screen).
Syntax | STDERR string_expression [;]
Remarks | Output to STDERR will normally be directed to the console screen unless it has been redirected. If the trailing semi-colon after string_expression is omitted, a carriage return and linefeed (CR/LF, $CRLF, or CHR$(13,10)) is sent immediately following string_expression.
See also | CONSIN, CONSOUT, GETSTDERR, GETSTDIN, GETSTDOUT, PRINT, STDEOF, STDIN LINE, STDOUT


STDIN LINE statement

Purpose

Read one line of text from the standard input device.

Syntax

STDIN LINE a$

Remarks

If data is available, it is placed in the a$ variable; otherwise, an empty is returned. You can keep reading data from STDIN, until the STDEOF function returns TRUE (non-zero). If input redirection is not present, STDIN LINE reads data from the keyboard until a CTRL+Z keystroke is detected.

Restrictions

STDIN LINE does not work with pipes.

See also

CONSIN, CONSOUT, GETSTDERR, GETSTDIN, GETSTDOUT, PRINT, STDEOF, STDERR, STDOUT

Example

WHILE NOT STDEOF ' loop until end-of-file or CTRL+Z is reached

STDIN LINE a$ ' read a line of data

a$ = UCASE$(a$) ' convert it to upper case

STDOUT a$ ' write it to STDOUT

WEND


STDOUT statement

Purpose

Output a string expression to the standard output device (console/screen).

Syntax

STDOUT string_expression [;]

Remarks

Output to STDOUT can be redirected to a file or a device. If redirection is not in effect, output is sent to the current active console page. If a PAGE statement is not in effect, STDOUT will write to console page 1.

If a trailing semi-colon after string_expression is not present, a carriage return and linefeed (CR/LF, $CRLF, or CHR$(13,10)) are sent immediately after string_expression.

Any control codes such as Carriage Return, Line Feed and Backspace embedded in string_expression will be interpreted appropriately if standard output is not redirected. That is, CR ($CR or CHR$(13)) causes the caret to move back to the beginning of the current line; LF ($LF or CHR$(10)) causes the caret to the next line; and Backspace ($BS or CHR$(8)) causes the caret to move back one character position.

If non-redirectable output is required, use the PRINT statement instead.

Restrictions

STDOUT is not compatible with Pipes. The current active page can be determined with the PAGEACTIVE function.

See also

CONSIN, CONSOUT, GETSTDERR, GETSTDIN, GETSTDOUT, PAGE, PAGEACTIVE, PAGEVISIBLE, PCOPY, PRINT, STDEOF, STDERR, STDIN LINE


STR$ function

Purpose

Return the representation of a number in printable form.

Syntax

s$ = STR$(numeric_expression [, digits])

Remarks

STR$ returns the string form of a variable or expression in printable text form. digits is an optional expression specifying the maximum total number of digits to appear in the result. If numeric_expression is greater than or equal to zero, STR$ adds a leading space character; if numeric_expression is less than zero, STR$ adds a leading negation (minus) character.

For example, STR$(14) returns a three-character string, of which the first character is a space, and the second and third are the ASCII characters "1" and "4". LTRIM$ can be used to remove leading space characters.

digits specifies the maximum number of significant digits (1 to 18) desired in the result.

STR$ can also be used to convert numeric values with more than 16 significant digits (i.e., Extended-precision floating-point and Quad-integers) to a printable form. For example:

a## = 2.0/3.0

x$ = STR$(a##,18)

returns 0.666666666666666667.

The complementary function is VAL, which takes a string argument and returns the numeric equivalent. Thus, number = VAL(STR$(number)).

An integer-class numeric value may also be converted to a string with the FORMAT$ and USING$ functions; however, FORMAT$ can easily return a string that is free from leading space characters. For example, x$ = FORMAT$(a&).

See also

BIN$, FORMAT$, HEX$, LTRIM$, OCT$, ROUND, USING$, VAL

Example

x$ = STR$(-1,5)

x$ = STR$(5/3,2)

x$ = STR$(5/3,8)

x$ = STR$(100000## / 3##, 18)

Result

-1

1.7

1.6666667

33333.3333333333333


STRDELETE$ function

Purpose

Delete a specified number of characters from a string expression.

Syntax

s$ = STRDELETE$(string_expression, start&, count&)

Remarks

Returns a based on copying string_expression, but with count& characters deleted starting at position start&. The first character in the string is position 1, etc.

See also

STRINSERT$, STRREVERSE$

Example

a$ = STRDELETE$("PowerBASIC", 4, 2)

Result

PowBASIC


STRING$ function

Purpose

Return a consisting of multiple copies of the specified character.

Syntax

s$ = STRING$(count, {code | string_expression})

Remarks

STRING$ with a numeric argument returns a string of count copies of the character with the ASCII code of code, where code is between 0 and 255, inclusive.

STRING$ with a string argument returns a string of count copies of the first character in string_expression.

The following functions all return a string of 8 spaces:

A$ = STRING$(8, 32)

A$ = STRING$(8, " ")

A$ = STRING$(8, $SPC)

A$ = SPACE$(8)

A$ = REPEAT$(8, " ")

A$ = REPEAT$(8, $SPC)

Use REPEAT$ to make multiple copies of a multiple-character string, and SPACE$ to return a string of space characters.

See also

ASC, CHR$, NUL$, REPEAT$, SPACE$


STRINSERT$ function

Purpose

Insert a at a specified position within another string expression.

Syntax

s$ = STRINSERT$(Main$, sNew$, position&)

Remarks

Returns a string consisting of the string expression Main$, with the string expression sNew$ inserted at position&. If position& is greater than the length of Main$, sNew$ is appended to Main$. The first character in the string is position 1, etc.

See also

CSET, CSET$, LSET, RSET, STRDELETE$, STRREVERSE$

Example

a$ = STRINSERT$("PowerBASIC", "ful", 6)

Result

PowerfulBASIC


STRPTR function

Purpose

Return the 32-bit DWORD address of the memory block used to store the data held by a dynamic (variable length) string.

Syntax

xPtr = STRPTR(StringVar)

Remarks

StringVar is the name of a string variable. STRPTR returns the 32-bit address in memory, where the contents of StringVar are stored.

Note that STRPTR differs from VARPTR. When used with a string variable, VARPTR returns the address of the string's handle, while STRPTR returns the address of the actual string data. Similarly, a STRING POINTER (or STRING PTR) is a pointer to a string handle - this important distinction should be recognized when working with the VARPTR and STRPTR operations.

STRPTR may not be used with fixed-length or ASCIIZ strings, because they do not use string handles. Use VARPTR with fixed-length and ASCIIZ strings.

When a dynamic string content is changed, the address of the string data will also change, but the string handle will remain in the same location. Therefore, it is important that your code refresh any pointers that target the string data memory locations directly.

See also

POKE$, PEEK$, VARPTR

Example

DIM x AS ASCIIZ PTR, A$

A$ = "PowerBASIC"

x = STRPTR(A$) ' address of the string handle

Message$ = A$ ' returns A$

Message$ = @x ' returns A$ as the target of x

A$ = "The power of BASIC!"

x = STRPTR(A$) ' Update string pointer address

Message$ = @x ' returns the target of x

Result

As the code above runs, Message$ is assigned the following strings:

PowerBASIC

PowerBASIC

The power of BASIC!


STRREVERSE$ function

Purpose

Reverse the contents of a expression.

Syntax

s$ = STRREVERSE$(Main$)

Remarks

Reverses the contents of Main$ and returns the result.

See also

STRDELETE$, STRINSERT$

Example

a$ = STRREVERSE$("PowerBASIC")

Result

CISABrewoP


SUB/END SUB statements

Purpose

Define a Sub block.

Syntax

[STATIC] SUB ProcName [BDECL | CDECL | SDECL] [ALIAS "AliasName"] [([arguments])] [EXPORT | PRIVATE] [STATIC]

[LOCAL variable_list]

[STATIC variable_list]

{statements}

[EXIT SUB]

{statements}

END SUB

Remarks

All executable code must reside in a Sub (or Function) block. You cannot define a Sub (or Function) inside another Sub or Function.

SUB and END SUB define a subroutine-like block of statements called a procedure (or subprogram), which is invoked with the CALL statement, and may be passed parameters by value or by reference.

A Sub may also be invoked without the use of the CALL statement. If the CALL statement is omitted, the parentheses around the arguments list must also be omitted.

STATIC

Specifies the default storage class for variables declared inside the Sub. If not specified, the default storage class is LOCAL. The STATIC keyword may appear before the SUB keyword, or after the list of arguments.

ProcName

The name of the Sub. ProcName must be unique: no variable, Function, Sub, or label can share the same name.

BDECL

Specifies that the declared procedure uses the BASIC/Pascal calling convention. When a procedure calls a BDECL procedure, it passes its parameters on the stack from left to right.

It is the responsibility of the called procedure to clean up the stack before returning to the calling procedure. Therefore, all PowerBASIC Subs and Functions that specify the BDECL convention automatically clean up the stack before execution returns to the calling code.

In the event the called procedure is imported or exported, PowerBASIC will automatically capitalize the function name unless an explicit ALIAS clause is specified. See ALIAS below.

CDECL

Specifies that the declared procedure uses the C calling convention. When a CDECL procedure is called, it passes its parameters on the stack from right to left.

The calling procedure removes any passed parameters from the stack as part of the return process. When PowerBASIC code calls Subs and Functions using the CDECL convention, the stack is cleaned automatically after execution returns from the called code.

In the event that the called procedure is imported or exported, PowerBASIC will automatically create a C style ALIAS for the function name. This alias will be prefixed with an underscore, followed by the original function name converted to lowercase.

The following two declarations are equivalent, indicating how the default ALIAS name would be created by PowerBASIC:

DECLARE SUB C_Function CDECL ()

DECLARE SUB C_Function CDECL ALIAS "_c_function" ()

SDECL

This is the default if neither BDECL nor CDECL are specified. SDECL (and its synonym STDCALL) specifies that the declared procedure uses the "Standard Calling Convention" as defined by Microsoft. When calling an SDECL procedure, parameters are passed on the stack right to left.

PowerBASIC Subs and Functions that use the SDECL/STDCALL convention automatically clean up the stack before execution returns to the calling code.

In the event the called procedure is imported or exported, PowerBASIC will automatically capitalize the function name unless an explicit ALIAS clause is specified.

ALIAS

String literal that identifies an case-sensitive alternative name for the procedure. This lets you export a Sub (or Function) by a name other than what it is called and referenced within the source code.

This can be useful if you want to abbreviate a long name, provide a more descriptive name, or if the exported name needs to contain characters that are illegal in PowerBASIC. AliasName is the routine's actual name as it appears in the export table, and ProcName is the title that you can use in PowerBASIC. For example:

SUB ShortName ALIAS "LongProcName" () EXPORT STATIC

The ALIAS clause is very important when exporting Subs and Functions. Omitting the ALIAS clause or incorrectly capitalizing the alias name are common causes of "Missing Export" errors. Please refer to the DECLARE and FUNCTION sections for more information.

Procedure definitions and program flow

The position of procedure definitions is mostly immaterial. They are usually grouped together in one region of the source code, but you cannot nest procedure definitions. That is, you cannot define a procedure within another procedure (although a procedure definition can contain calls to other procedures and functions, etc).

Unlike subroutines (see GOSUB), program execution cannot accidentally "fall into" a procedure, even if it is located before the PBMAIN or WINMAIN Function in your code. For example:

#COMPILE EXE

SUB DisplayInfo(a$)

' Code goes here

END SUB

...

FUNCTION PBMAIN

' Main program code goes here

END FUNCTION

When this program is executed, the code in DisplayInfo is only executed if the procedure is explicitly called, even though it is located earlier in the source code file.

Procedure definitions should be treated like isolated islands of code; do not jump in or out of them with GOTO, GOSUB or RETURN. Within a procedure block, such statements are legal.

Parameters

arguments

An optional, comma-delimited sequence of formal parameters. The parameters used in the arguments list serve only to define the procedure; they have no relationship to other variables in the program (outside of the procedure) with the same name. Including a type class keyword (STATIC, or LOCAL) at the end of the SUB header can specify the default variable type within the SUB body. If no keyword is included, the default is LOCAL.

Normally, PowerBASIC passes parameters to a procedure either by reference or by copy. Either way, the address of a variable is passed, and the procedure has to look at that address to get the value of the parameter. If you do not need to modify the parameter (true in many cases), you can speed up your procedures by passing the parameter by value using the BYVAL keyword with your parameter name.

The type of the parameter is specified either by including a type-declaration character at the end of the name or using an AS clause. For example:

SUB Test(A AS INTEGER) ' integer passed by reference

SUB Test(A%) ' integer passed by reference

SUB Test(BYREF A%) ' integer passed by reference

SUB Test(BYVAL A%) ' integer passed by value

PowerBASIC compilers have a limit of 32 parameters per SUB. To pass more than 32 parameters to a SUB, construct a User-Defined Type (UDT) and pass the address of the UDT by reference (BYREF) instead.

Fixed-length strings, ASCIIZ strings, and User-Defined Types/Unions may also be passed as BYVAL or OPTIONAL parameters, now. Try to avoid passing large items BYVAL, as it’s terribly inefficient, and there is a maximum size limit of 64 Kb for a given parameter list.

When a Sub definition specifies either a BYREF parameter or a pointer variable parameter, the calling code may freely pass a BYVAL DWORD or a pointer instead. While the use of the explicit BYVAL override in the calling code is optional, it is recommended for clarity. It is necessary to explicitly declare all pointer parameters as BYVAL. Failure to do so will generate a compile-time Error 549 ("BYVAL required with pointers"). For example:

SUB DoPtrMath(BYVAL x AS BYTE PTR)

A Sub may be imported and exported within the same module. That is, a function in the module may be stated as EXPORT, while a DECLARE in the same module specifies it as an imported SUB by the option LIB "filename.dll", provided FILENAME.DLL is the name of the module. This may be particularly valuable when you wish to build an #INCLUDE file with all of the DECLARE statements for a project.

Additional information on BYVAL/BYREF/BYCOPY parameter passing can be found in the CALL statement topic.

Optional parameters

PowerBASIC now supports two syntax formats for optional parameters: the classic optional parameter syntax using brackets "[..]", and the new syntax using the OPTIONAL (or OPT) keyword. We'll discuss each one in turn.

Using OPTIONAL/OPT

SUB statements may specify one or more parameters as optional by preceding the parameter with either the keyword OPTIONAL (or the abbreviation OPT). Optional parameters are only allowed with CDECL or SDECL calling conventions, not BDECL.

When a parameter is declared optional, all subsequent parameters in the declaration are optional as well, whether or not they specify an explicit OPTIONAL or OPT directive. The following two lines are equivalent, with both second and third parameters being optional:

SUB sABC(a&, OPTIONAL BYVAL b&, OPTIONAL BYVAL c&)

SUB sABC(a&, OPT BYVAL b&, BYVAL c&)

When optional parameters are omitted in the calling code, the stack area normally reserved for those parameters is zero-filled. This allows you to test if an optional parameter was passed or not:

If the parameter is defined as a BYVAL parameter, it will have the value zero. For TYPE or UNION variables passed BYVAL, the compiler will pass a string of binary zeroes of length SIZEOF(Type_or_union_var).

If the parameter is defined as a BYREF parameter, VARPTR (varname) will equal zero; when this is true, any attempt to use Var_name in your code will result in Error #9 (null pointer); failure to detect this error using error-trapping may result in a General Protection Fault or memory corruption.

The OPTIONAL directive provides the same functionality as the older syntax using square brackets "[..]". See below.

Using classic optional parameters

When declaring a CDECL SUB or FUNCTION, you can specify trailing parameters as optional, using a set of brackets [..]:

SUB KerPlunk CDECL (x%, y% [, z%])

Note that the comma separating the y% parameter from the optional z% parameter is inside the brackets. The following calling sequences would then be valid:

CALL KerPlunk (x%, y%)

CALL KerPlunk (x%, y%, z%)

Optional parameters must be the last parameters designated in the list. The following is invalid:

SUB KerPlunk CDECL ([x%,] y%, z%)

Because the SUB (or FUNCTION) being called does not know how many parameters are being passed at the time it is called, you should pass the number of parameters as one of the required parameters in the list.

PowerBASIC continues to support the use of classic optional parameter syntax using brackets ([..]) but this will not be the case in future versions of PowerBASIC. Existing code should be changed to the new OPTIONAL syntax as soon as possible to ensure compatibility with future versions of PowerBASIC.

Local variables

By default, all undeclared variables in a procedure are LOCAL (unless you have specified STATIC in the procedure header). You can also use the LOCAL statement before any executable statements in the Sub definition, to explicitly declare local variables. Since this default behavior is subject to change, you should make an effort to declare every variable used in a procedure. For example:

SUB MySub()

LOCAL a%, b#, BigArray%()

...

END SUB

creates three local variables: scalar variables a% and b# (Integer and Double-precision, respectively), and the Integer array BigArray%. The array must then be dimensioned appropriately:

DIM BigArray%(1000)

Local variables and arrays variables are automatically deallocated when the procedure terminates. LOCAL scalar variables (except dynamic strings) are stored on the stack, so creating large ASCIIZ or fixed-length strings (say, approaching 1 MB) may cause a stack overflow. In this case, use STATIC or GLOBAL ASCIIZ or fixed-length strings, or change the code to use LOCAL dynamic (variable-length) strings.

Static variables

Static variables retain their values as long as the program is running. To declare static variables within a procedure, use the STATIC statement before any executable statements in the definition.

Use the GLOBAL statement to declare variables that are global to the rest of the program.

You must terminate a procedure definition with END SUB, which returns control to the statement directly after the invoking CALL. Use the EXIT SUB statement to return from a procedure definition before reaching the END SUB statement.

Exported procedures

The EXPORT attribute makes a procedure available from another program (DLL or EXE). This is similar to the "PUBLIC" keyword used by some other programming languages.

EXPORT

Subs are private by default. The EXPORT keyword can be used to make a Sub accessible from another module (a DLL). Exported Subs and Functions can only be imported from a DLL, not from another .EXE.

PRIVATE

Subs are private by default. The PRIVATE keyword is not necessary, but may be used to help make your code more readable.

See also

CALL, DECLARE, EXIT SUB, FUNCNAME$, FUNCTION/END FUNCTION, GLOBAL, GOSUB, LOCAL, RETURN, STATIC

Example

SUB TestProcedure(I%, L&, S!, D#, E##, A())

.

.' Code to process parameters

.

END SUB ' end procedure TestProcedure

DIM MyArray(20) ' declare array of numbers

IntegerVar% = 1

LongInt& = 2

SinglePre! = 3

DoublePre# = 4

MyArray(3) = 5

CALL TestProcedure(IntegerVar%, LongInt&, SinglePre!, DoublePre#, IntegerVar%^2, MyArray())


SWAP statement

Purpose

Exchange the values of two variables of the same .

Syntax

SWAP var1, var2

Remarks

var1 and var2 are two variables of the same type. If you try to swap variables of differing types (for example, and , or Single-precision and Double-precision), a compile-time Error 482 occurs ("Data type mismatch").

SWAP is handy because a simple trading of values in two consecutive assignment statements does not get the job done:

a = b

b = a

By the time you make the second assignment, variable a does not contain the value it used to. To do this without the SWAP statement requires a temporary variable and a third assignment:

temp = a

a = b

b = temp

SWAP can be used to swap the target values of pointers. In addition, SWAP can also be used to swap the values of pointers themselves.


SWITCH function

Purpose

Return one of a series of values based upon a TRUE/FALSE evaluation of a corresponding series of expressions.

Syntax

var = SWITCH(expr1, val1 [[, expr2, val2], ...])

var& = SWITCH&(expr1, val1& [[, expr2, val2&], ...])

var$ = SWITCH$(expr1, val1$ [[, expr2, val2$], ...])

Remarks

SWITCH expects values of any type. SWITCH& expects values optimized for Long-integer type. SWITCH$ expects values of type.

If expr1 evaluates TRUE, val1 is returned, if expr2 evaluates TRUE, val2 is returned, etc.

Each control expression in the series is evaluated as a typical PowerBASIC Boolean expression, which offers short-circuit expression evaluation as needed. To force a bitwise evaluation of an expression, enclose it in parentheses. The value parameters may be expressions, literals or variables of the appropriate data type for the SWITCH function in use.

SWITCH returns the matching value parameter from the first TRUE evaluation of the control expressions, evaluated from left to right in the list. Therefore, it would be wise to place the most likely selections at the front of the SWITCH list to achieve the utmost efficiency.

Restrictions

Contrary to the implementation in some other languages, only the chosen value (one of val1, val2, val3…) is evaluated at run-time; the other value parameters are not. This ensures optimum execution speed, as well as the elimination of unanticipated side effects.

See also

CHOOSE, IF, IIF, SELECT

Example

' SWITCH with simple expressions

A$ = SWITCH$(x%=1, "Bob", x%=20, "Bruce", x% > 20, "Dan", x% < 1, "Nobody!")

' SWITCH with complex expressions

FUNCTION z(i&) AS LONG

INCR i&

FUNCTION = i&

END FUNCTION

FUNCTION PBMAIN

x& = -1

Choice& = SWITCH&(z(x&), 1, z(x&), 2, z(x&), 3)

' Choice& will equal 2

END FUNCTION


TAB$ function

Purpose

Return a with embedded TAB ($TAB) characters expanded with spaces to a given tab stop.

Syntax

sResult$ = TAB$(strtotab$, tabstop&)

Remarks

All TAB (CHR$(9) or $TAB) characters in strtotab$ are replaced with spaces to pad the resulting string to the tab stop position specified in tabstop&. strtotab$ and tabstop& may be variables, literals, or expressions.

Restrictions

If the tab stop specified in tabstop& is less than 1 or greater than 256, the original string is returned unchanged.

See also

PARSE$, REPLACE

Example

a$ = "Hello" & $TAB & "World" & $TAB & _

"From PB, Inc."

b$ = TAB$(a$,8)

Result

b$ contains "Hello World From PB, Inc."


TALLY function

Purpose

Count the number of occurrences of specified characters or strings within a .

Syntax

x& = TALLY(MainString, [ANY] MatchString)

Remarks

MainString is the string expression in which to count characters. MatchString is the string expression to count all occurrences of. If MatchString is not present in MainString, zero is returned.

ANY

If the ANY keyword is included, MatchString specifies a list of single characters to be searched for individually: a match on any one of which will cause the count to be incremented for each occurrence of that character. Note that repeated characters in MatchString will not increase the tally. For example:

X = TALLY("ABCD", ANY "BDB") ' returns 2, not 3

Restrictions

TALLY is case-sensitive, so be wary of capitalization.

See also

INSTR, JOIN$, LCASE$, LTRIM$, MID$, PARSE, PARSE$, PARSECOUNT, REMOVE$, REPLACE, RIGHT$, RTRIM$, TRIM$, UCASE$, VERIFY

Example

' Returns 1, counting the string "bac"

x& = TALLY("abacadabra", "bac")

'returns 8, counting all "b", "a", and "c" characters

x& = TALLY("abacadabra", ANY "bac")


TAN function

Purpose | Return the tangent of its argument.
Syntax | y = TAN(numeric_expression)
Remarks | numeric_expression is an angle specified in radians. To convert radians to degrees, multiply by 57.29577951308232##. To convert degrees to radians, multiply by 0.0174532925199433##. For more information on radians, see ATN. TAN returns an Extended-precision result. TAN is approximated with the expression: TAN = SIN(Value) / COS(Value) The Inverse Tangent (ARCTAN) of a value can be easily calculated with the ATN function. The Hyperbolic Tangent (TANH) of a value can be calculated: TanH = (EXP(2 * Value) - 1) / (EXP(2 * Value) + 1) The Inverse Hyperbolic Tangent (ARCTANH) of a value can be calculated: ArcTanH = LOG((1 + Value) / (1 - Value)) / 2 ' Useful Macro functions MACRO Pi = 3.141592653589793## MACRO DegreesToRadians(dpDegrees) = (dpDegrees * 0.0174532925199433##) MACRO RadiansToDegrees(dpRadians) = (dpRadians * 57.29577951308232##)
See also | ATN, COS, SIN
Example | pi# = 3.141592653589793## FOR I& = 5 TO 45 STEP 5 x$ = "The Tangent of " + FORMAT$(I&,"* ") + _ " degrees = " + FORMAT$(TAN(pi## / 180 * _ I&),"0.00") NEXT I&
Result | The Tangent of 5 degrees = 0.09 The Tangent of 10 degrees = 0.18 The Tangent of 15 degrees = 0.27 The Tangent of 20 degrees = 0.36 The Tangent of 25 degrees = 0.47 The Tangent of 30 degrees = 0.58 The Tangent of 35 degrees = 0.70 The Tangent of 40 degrees = 0.84 The Tangent of 45 degrees = 1.00


TCP ACCEPT statement

Purpose | Accept an incoming request for communication from a specified TCP/IP port.
Syntax | TCP ACCEPT [#] fNum& AS newfNum&
Remarks | Accept an incoming connection request to the fNum& socket, and create a newfNum& socket handle to communicate with the new connection. TCP ACCEPT is only valid with sockets opened using TCP OPEN SERVER.
See also | TCP and UDP communications, TCP CLOSE, TCP LINE INPUT, TCP NOTIFY, TCP OPEN, TCP PRINT, TCP RECV, TCP SEND, UDP OPEN


TCP CLOSE statement

Purpose | Close a previously opened TCP/IP port.
Syntax | TCP CLOSE [#] fNum&
Remarks | Close the previously opened TCP/IP port specified by fNum&.
See also | TCP and UDP communications, TCP ACCEPT, TCP LINE INPUT, TCP NOTIFY, TCP OPEN, TCP PRINT, TCP RECV, TCP SEND, UDP CLOSE


TCP LINE INPUT statement

Purpose

Receive a line of text from a specified TCP/IP port.

Syntax

TCP LINE [INPUT] [#] fNum&, Buffer$

Remarks

Receive a line of text from the fNum& TCP/IP port, and place the data in Buffer$. If no bytes are available, Buffer$ will be empty (a null string). If TCP LINE did not receive a complete line of text (terminated by a $CRLF character pair), EOF(fNum&) will return TRUE (non-zero).

If a time-out occurs, ERR will be set to indicate a run-time Error 24 ("Device timeout"). See TCP OPEN to specify the TCP socket timeout value.

The EOF function may also be used with TCP LINE (and COMM LINE) to detect that an incomplete line was received. Normally, the TCP LINE statement reads data until a $CRLF character pair is found, and in that case, EOF will return false (zero). However, even if no $CRLF has been found, TCP LINE will return if no additional data is available. In that case, TCP LINE will return whatever data has been accumulated, and set EOF to logical TRUE (non-zero).

In many cases, it would be prudent to test EOF after every TCP LINE statement to verify that a full line has been received. In some cases, you may wish to execute the statement one or more additional times, combining the data, in order to obtain a full line of text.

See also

TCP and UDP communications, EOF, TCP ACCEPT, TCP CLOSE, TCP NOTIFY, TCP OPEN, TCP PRINT, TCP RECV, TCP SEND, UDP RECV


TCP NOTIFY statement

Purpose

Designate which TCP/IP events will generate a notification message.

Syntax

TCP NOTIFY [#] fNum&, {SEND | RECV | ACCEPT | CONNECT | CLOSE} TO hWnd& AS wMsg&

Remarks

Designates which events (SEND, RECV, ACCEPT, CONNECT and CLOSE) will generate a wMsg& notification message to the window procedure (callback) of the GUI window or dialog whose window handle is contained in hWnd&.

Your program defines the wMsg& value, and this value should be equal or larger than %WM_USER + 500 to avoid conflict with other (common) callback message values.

When the nominated callback function receives the wMsg& notification, the wParam& parameter identifies the operating system's handle of the socket (see FILEATTR), the low-order Word of lParam& specifies the code of the event (see table below), and the high-order Word of lParam& contains the error code (if any).

LO(WORD, lParam&) | Definition
%FD_READ | Data is available to be read from the socket.
%FD_WRITE | The socket is ready for data to be written.
%FD_ACCEPT | The socket is able to accept a new connection.
%FD_CONNECT | The connection has been established.
%FD_CLOSE | The socket has been closed.

Notification messages do not arrive in unabated or continuous streams. That is, once a particular notification message arrives, it will not be sent again until the initial message is acted upon. For example, if a %FD_READ notification is received, it will not be resent until after a TCP RECV statement is executed.

The Winsock error codes are listed in WS2_32.INC, prefixed with %WSAE.

See also

TCP and UDP communications, TCP ACCEPT, TCP CLOSE, TCP LINE INPUT, TCP OPEN, TCP PRINT, TCP RECV, TCP SEND, UDP NOTIFY


TCP OPEN statement

Purpose | Enable an application to communicate with a TCP/IP server or client using the TCP protocol over Winsock.
Syntax | As a client: TCP OPEN {PORT p& | srvc$} AT host$ AS [#] fNum& [TIMEOUT timeoutval&] As a server: TCP OPEN SERVER [ADDR ip&] {PORT p& | srvc$} AS [#] fNum& [TIMEOUT timeoutval&]
Remarks | Open a TCP/IP port or service for communication, either as a client or as a server.
SERVER | If the keyword server is included, the TCP port is opened as a TCP/IP server; otherwise, it is opened as a TCP/IP client.
ADDR ip& | As a server, if you specify the optional ADDR ip&, the TCP server monitors connections at the specified ip& address. Otherwise, the primary IP address for the computer is used by default.
PORT p& | As a client, PORT identifies the server port that the client attempts to connect to. As a server, PORT identifies the port the server will monitor for connection requests. You may specify either a port number or a service name, but not both.
srvc$ | If the port number is not specified, a service name must be specified instead. A service name takes the form of "http", "smtp", or "ftp", etc. You may specify either a port number or a service name, but not both.
AT host$ | As a client, host$ identifies the host to connect with. host$ can be a domain such as "powerbasic.com", or a dotted IP address in string form, such as "127.0.0.1".
fNum& | A file number such as #1, or a variable with a value obtained using the FREEFILE function.
TIMEOUT | The optional TIMEOUT value allows you to specify how long a TCP SEND, RECV, PRINT, or LINE operation should wait for completion, in milliseconds (mSec). If the specified number of milliseconds elapses without a response, the TCP operation will fail, and the ERR system variable will be set to indicate a run-time Error 24 ("Device timeout"). The default timeout is 60000 milliseconds (60 seconds).
See also | TCP and UDP communications, FREEFILE, TCP ACCEPT, TCP CLOSE, TCP LINE INPUT, TCP NOTIFY, TCP PRINT, TCP RECV, TCP SEND, UDP OPEN
Example | ' Client TCP/IP example - retrieve a web page #COMPILE EXE FUNCTION PBMAIN() AS LONG LOCAL Buffer$, Site$ LOCAL Entire_page$, Htmlfile$, Link$ LOCAL Pos&, Length& Site$ = "www.powerbasic.com" File$ = "http://www.powerbasic.com/support/forums/Forum2/HTML/000031.html" ' Connecting... TCP OPEN "http" AT Site$ AS #1 TIMEOUT 60000 ' Could we connect to site? IF ERR THEN BEEP EXIT FUNCTION END IF ' Send the GET request... TCP PRINT #1, "GET " & File$ & " HTTP/1.0" TCP PRINT #1, "Referer: http://www.powerbasic.com/" TCP PRINT #1, "User-Agent: TCP OPEN Example (www.powerbasic.com) TCP PRINT #1, "" ' Retrieve the page... DO TCP RECV #1, 4096, Buffer$ Entire_page = Entire_page + Buffer$ LOOP WHILE ISTRUE LEN(Buffer$) AND ISFALSE ERR ' Close the TCP/IP port... TCP CLOSE #1 END FUNCTION


TCP PRINT statement

Purpose

Write a string to a nominated TCP/IP port.

Syntax

TCP PRINT [#] fNum&, string_expression[;]

Remarks

Write the data in string_expression to the fNum& TCP/IP port. If the optional semi-colon is not specified, a carriage-return and linefeed pair ($CRLF or CHR$(13,10)) is also sent.

The TCP PRINT statement does not return until string_expression has been sent, or an error occurs. That is, TCP PRINT is a synchronous or "blocking" statement. If a time-out occurs, ERR will be set to indicate a run-time Error 24 ("Device timeout"). See TCP OPEN to specify the TCP socket timeout value.

See also

TCP and UDP communications, TCP ACCEPT, TCP CLOSE, TCP LINE INPUT, TCP NOTIFY, TCP OPEN, TCP RECV, TCP SEND, UDP OPEN


TCP RECV statement

Purpose

Receive data from a specified TCP/IP port.

Syntax

TCP RECV [#] fNum&, count&, Buffer$

Remarks

Receive count& bytes from the fNum& TCP/IP port and place them in Buffer$. If count& bytes are not available, Buffer$ will receive whatever bytes are available and EOF(fNum&) will return TRUE (non-zero).

Typically used in a to retrieve a stream of data, a TCP RECV loop should be terminated if Buffer$ returns an empty , or if EOF(fNum&) returns TRUE, or if ERR becomes set. If a time-out occurs, ERR will be set to indicate a run-time Error 24 ("Device timeout"). See TCP OPEN to specify the TCP socket timeout value.

See also

TCP and UDP communications, EOF, TCP ACCEPT, TCP CLOSE, TCP LINE INPUT, TCP NOTIFY, TCP OPEN, TCP PRINT, TCP SEND, UDP RECV


TCP SEND statement

Purpose | Write a string to a nominated TCP/IP port.
Syntax | TCP SEND [#] fNum&, string_expression
Remarks | Write the specified string_expression to the TCP/IP port specified by fNum&. The TCP SEND statement does not return until string_expression has been sent, or an error occurs. That is, TCP SEND is a synchronous or "blocking" statement. If a time-out occurs, ERR will be set to indicate a run-time Error 24 ("Device timeout"). See TCP OPEN to specify the TCP socket timeout value.
See also | TCP and UDP communications, TCP ACCEPT, TCP CLOSE, TCP LINE INPUT, TCP NOTIFY, TCP OPEN, TCP PRINT, TCP RECV, UDP SEND


THREAD CLOSE statement

Purpose

Close a Windows thread, or release the handle of a running thread.

Syntax

THREAD CLOSE hThread TO lResult&

Remarks

THREAD CLOSE releases the thread handle of the thread identified by the DWORD value hThread (see THREAD CREATE).

If successful, lResult& is TRUE (non-zero); otherwise, it is FALSE (zero). If a thread is not closed once it has completed, it will continue to take up memory and CPU resources. Note that THREAD CLOSE does not stop a thread if it is still running; it simply releases the thread's handle (i.e., the resources used to track the thread), and the thread itself will continue to run.

Once a thread handle is released, the value stored in hThread becomes undefined. On this basis, thread handles should not be released until there is no further need to test the thread status or change the suspend count for a thread. If a thread does not need to be monitored, its handle can be released immediately after the THREAD CREATE statement, and the threads resources will be freed automatically when the thread terminates naturally. Best practice suggests that after releasing a thread handle, the thread handle variable should be set to 0, to set it apart from other valid thread handle variables.

Once a thread has exited, it is not possible to restart the same thread (as identified by hThread). However, a fresh thread can be executed, using the same target thread Function, and resulting in a new thread handle which will identify the new thread.

Restrictions

THREAD CLOSE will always execute successfully provided hThread contains a valid thread handle value. THREAD CLOSE generates no run-time errors; all exceptions are reported in the return value lResult&.

The WaitForSingleObject API function can be used wait until a nominated thread has finished executing. Similarly, the WaitForMultipleObjects API can be used to wait for one, two, or all secondary threads (to a maximum of 64 or % MAXIMUM_WAIT_OBJECTS) to complete before continuing on. Such functions can be very useful when a program creates a set of "worker" threads to process data, and the primary thread can then sit idle until all the worker threads have completed all their work. At that point, the primary thread may gather the results of the worker threads, etc.

It is also useful to understand that these kind of wait functions are very efficient and use almost no CPU time or resources while they are waiting; however, care must be exercised to avoid a deadlock or circular suspension. For example, a deadlock condition could occur if thread A is halted while it waits for thread B, which in turn has a suspend count that might only be adjustable by Thread A. Similarly, an infinite loop in one thread may also halt any other thread that is waiting for it to terminate.

THREADCOUNT continues to report a thread tally that will include threads whose handle has already been released. A thread ID value may not be used interchangeably with a thread handle value.

See also

FUNCTION/END FUNCTION, THREAD CREATE, THREAD RESUME, THREAD STATUS, THREAD SUSPEND, THREADCOUNT, THREADED variables, THREADID


THREAD CREATE statement

Purpose

Create a Windows thread. Threads provide powerful ways for an application to perform several tasks at the same time.

Syntax

THREAD CREATE ProcName (param) [SUSPEND] TO hThread

Remarks

THREAD CREATE creates and begins execution of a new thread Function identified by ProcName. ProcName is specified without quotation marks. This function must take exactly one Long-integer or Double-word (DWORD) parameter by value (BYVAL). For example:

FUNCTION MyThreadFunction(BYVAL x AS LONG) AS LONG

' Thread code goes here

END FUNCTION

...

THREAD CREATE MyThreadFunction(var&) TO hThread???

The 32-bit parameter passed to the thread may be used to pass a value such as a programmer-defined ID or window handle to post "progress" messages back to a GUI window/dialog running in another thread. A more common use for the parameter is to pass the address to a UDT or other data structure. Passing an address this way can enable the thread to use a pointer to access large volumes of data that reside outside of the thread. For example:

FUNCTION MyThread(BYVAL y AS DWORD) AS DWORD

DIM x AS MyUDT POINTER

x = y ' Set the pointer from the DWORD param

' From here we can access all of the UDT member elements using the standard @x pointer syntax

END FUNCTION

...

DIM x AS MyUDT, hThread???

' Initialize the members of x here

THREAD CREATE MyThread( VARPTR(x) ) TO hThread???

...

Note that data passed this way is subject to the notes (below) concerning GLOBAL and STATIC variables, in order to avoid synchronization problems during context-switching.

The return value of the thread Function is retrieved with the THREAD STATUS statement (once the thread has completed execution).

SUSPEND

Execution of the thread begins immediately unless the SUSPEND option is included. In that case, the suspend count for the thread will be initially set to 1, and the thread will be initially suspended. The THREAD RESUME statement is used to decrease the suspend count of a thread by 1, and when the suspend count reaches 0, the thread will start (resume) execution. Controlling the suspend state of a thread requires the thread handle value be retained until such time as the thread can be closed or left to run unmonitored.

hThread

If successful, THREAD CREATE returns a Double-word (or Long-integer) handle in hThread, or zero (0) if the thread was not started. This handle is used with the other to control the suspend count, and to release the thread handle, etc. Also see THREAD CLOSE for more information on monitoring, closing, and waiting for threads to complete.

ProcName

The name of the thread function to execute as a thread. A thread Function must comply exactly with the following syntax:

FUNCTION ThreadFuncName (BYVAL param AS {LONG | DWORD}) AS {LONG | DWORD}

Restrictions

The THREAD CREATE statement generates no run-time errors; all exceptions are reported as a zero stored in the return value hThread. However, the target thread Function must be located in the same compiled module as the THREAD CREATE statement. That is, a thread Function may not be an imported Function.

Additionally, a thread Function may not be directly called or executed, except by a THREAD CREATE statement. This restriction is imposed to ensure that PowerBASIC run-time library can maintain a thread-safe state at all times, correctly allocate and deallocate internal thread-local storage, and the various (such as THREADCOUNT) can return accurate values.

One situation that can arise is where a Function may need to be invoked both directly and used as a thread Function. The easiest solution is to create a small wrapper Function for the Function, then use THREAD CREATE with the wrapper Function when a thread is required, or continue to call the original Function directly when a separate thread is not required. For example:

FUNCTION WorkerFunc(BYVAL x AS LONG) AS LONG

' code here

END FUNCTION

FUNCTION WorkerThread(BYVAL x AS LONG) AS LONG

FUNCTION = WorkerFunc(x)

END FUNCTION

...

' Execute the worker function directly, thus:

lResult& = WorkerFunc(var&)

' Execute the worker thread as a thread, using

' the wrapper function:

THREAD CREATE WorkerThread(var&) TO hThread???

A thread can determine its own ID with the THREADID function. Note: a thread ID is not interchangeable with a thread handle.

Threads are initialized and started asynchronously, so it is wise to give the operating system a small amount of time to perform thread initialization before using the THREADCOUNT function to monitor the thread.

Once a thread has exited, it is not possible to restart the same thread as identified by hThread - however, a new thread can be initiated using the same Function (which naturally provides a new hThread handle value). In addition, the same thread Function can be launched multiple times to create a set of identical threads executing the same code.

As each thread is created, it is assigned its own "private" stack frame. Therefore, LOCAL and REGISTER variables are private to each thread, and are automatically "thread-safe".

Exercise care when using GLOBAL and STATIC variables that may be accessed by more than one thread at the same time. If one thread is part way through storing data at the point where another thread begins to read the same memory block, it can result in the second thread reading only partially updated (i.e., invalid) data. The point where one thread is suspended so that another can run is called a "context-switch". In these situations, the use of Windows' synchronization functions (such as Critical Sections and Mutexes) may be employed to create thread-safe code.

Thread-safe code is deemed to be unaffected by context-switching, regardless of when context-switching occurs. Local variables, being stored in a "private" stack frame, are not affected by context-switching.

Local variable storage created by each thread is automatically freed when the thread Function terminates, in the same manner as a normal Sub or Function. However, the thread handle must be explicitly freed with a THREAD CLOSE statement. The THREAD CLOSE can occur at any time, since it only frees the thread handle and has no other impact on the running thread. If the thread result value is not required (or the thread state does not need to be altered), THREAD CLOSE can be used immediately after the THREAD CREATE statement, leaving the thread to run its course.

For more information on threading and synchronization techniques, please refer to MSDN http://msdn.microsoft.com.

The PowerBASIC run-time library is thread-safe and reentrant.

See also

FUNCTION/END FUNCTION, THREAD CLOSE, THREAD RESUME, THREAD STATUS, THREAD SUSPEND, THREADCOUNT, THREADED variables, THREADID

Example

SUB SpawnThreads()

LOCAL x AS LONG

LOCAL s AS LONG

DIM hThread(10) AS LOCAL DWORD

FOR x = 1 TO 10

THREAD CREATE MyThread(x) TO hThread(x)

SLEEP 50

NEXT

DisplayText "10 Threads Started! " + _

"Wait for them to finish!"

DO

FOR x = 1 TO 10

SLEEP 0

THREAD STATUS hThread(x) TO s

IF s <> &H103 AND s <> 0 THEN ITERATE DO

NEXT

LOOP WHILE s

FOR x = 1 TO 10

THREAD CLOSE hThread(x) TO s

NEXT x

DisplayText "Finished!"

END SUB

' The following is executed as a thread Function!

FUNCTION MyThread (BYVAL x AS LONG) AS LONG

LOCAL n AS LONG

LOCAL t AS SINGLE

DisplayText "Begin Thread" + STR$(x)

t = TIMER

FOR n = 1 TO 10

SLEEP 100 + 100 * x

NEXT n

t = TIMER - t

DisplayText "End Thread" + STR$(x) + _

" Elapsed time = " + STR$(t,5)

END FUNCTION


THREAD RESUME statement

Purpose

Resume execution of a Windows thread.

Syntax

THREAD RESUME hThread TO lResult&

Remarks

THREAD RESUME decreases the suspend count of the thread identified by the 32-bit DWORD value stored in hThread (see THREAD CREATE). If it succeeds, the lResult& value is the thread's previous suspend count; otherwise, it is -1.

Execution of a suspended thread resumes when the suspend count of a thread is decremented to zero. If the SUSPEND option is included in the associated THREAD CREATE statement, the thread will have an initial suspend count of 1. In that case, execution of the thread will only begin when a THREAD RESUME statement is executed, using the thread handle stored in hThread to identify the thread.

Restrictions

The THREAD RESUME statement generates no run-time errors; all exceptions are reported in the return value lResult&. A thread ID cannot be used interchangeably with a thread handle. A thread can suspend itself by incrementing its own suspend count, but logically, cannot decrement its own suspend count.

See also

FUNCTION/END FUNCTION, THREAD CLOSE, THREAD CREATE, THREAD STATUS, THREAD SUSPEND, THREADCOUNT, THREADED variables, THREADID


THREAD STATUS statement

Purpose

Retrieve the Status of a Windows thread.

Syntax

THREAD STATUS hThread TO lResult&

Remarks

THREAD STATUS assigns the status of the thread identified by the DWORD value in hThread (see THREAD CREATE) to lResult&.

If the function fails, lResult& is set to zero. If the thread is still running, the system value &H103 is assigned. If the thread has terminated and the thread handle has not yet been closed, the return value from the thread Function is assigned to lResult&. To wait for one or more threads to complete execution, use the WaitForSingleObject or WaitForMultipleObjects API functions - see THREAD CLOSE for more information.

The number of currently running threads in a module can be determined with the THREADCOUNT function.

Restrictions

The THREAD STATUS statement generates no run-time errors; all exceptions are reported in the return value lResult&. A thread ID cannot be used in place of a thread handle.

See also

FUNCTION/END FUNCTION, THREAD CLOSE, THREAD CREATE, THREAD RESUME, THREAD SUSPEND, THREADCOUNT, THREADED variables, THREADID


THREAD SUSPEND statement

Purpose

Suspend execution of a Windows thread.

Syntax

THREAD SUSPEND hThread TO lResult&

Remarks

THREAD SUSPEND adds 1 to the suspend count of the thread specified by hThread (see THREAD CREATE). If it succeeds, the lResult& value is the thread's previous suspend count; otherwise, it is -1. A thread is always suspended if it has a suspend count of 1 or higher.

To decrement the suspend count of a thread, use the THREAD RESUME statement. A suspended thread will only resume execution when its suspend count is decremented to 0.

Restrictions

The THREAD SUSPEND statement generates no run-time errors; all exceptions are reported in the return value lResult&. A thread ID cannot be used interchangeably with a thread handle. A thread can suspend itself by incrementing its own suspend count.

See also

FUNCTION/END FUNCTION, THREAD CLOSE, THREAD CREATE, THREAD RESUME, THREAD STATUS, THREADCOUNT, THREADED variables, THREADID


THREADCOUNT function

Purpose

Return the number of PowerBASIC-created active threads that exist in a module.

Syntax

lCount& = THREADCOUNT

Remarks

Applications will return a THREADCOUNT of at least 1, which is attributed to the "primary" application thread. Additional threads created by the application or module with the THREAD CREATE function will also be included in the tally returned by THREADCOUNT.

THREADCOUNT can be useful for when a "controlling thread" needs to poll the state of a collection of "worker threads" as they complete a set of tasks. However, care should be exercised if other (unrelated) threads may also be running in the same module - in such cases, using THREAD STATUS is the preferred solution. If polling is not desired, the WaitForMultipleObjects API function can also be useful - see THREAD CLOSE for more information.

Restrictions

THREADCOUNT includes threads that have had their thread handle released with THREAD CLOSE, yet are still running.

Threads are initialized and started asynchronously, so it is wise to give the operating system a small amount of time to perform thread initialization before using the THREADCOUNT function to monitor the thread.

A thread Function may not be directly called or executed, except by a THREAD CREATE statement. This restriction is imposed to ensure that PowerBASIC run-time library can maintain a thread-safe state at all times, correctly allocate and deallocate internal thread-local storage), and functions such as THREADCOUNT can return accurate values. See THREAD CREATE for more information and solutions.

See also

FUNCTION/END FUNCTION, THREAD CLOSE, THREAD CREATE, THREAD RESUME, THREAD STATUS, THREAD SUSPEND, THREADED variables, THREADID

Example

FUNCTION tZ(BYVAL x&) AS LONG

' Wait for a random time

SLEEP x& * RND(1,1000)

FUNCTION = 1

END FUNCTION

FUNCTION PBMAIN

' Create 10 threads

FOR x& = 1 TO 10

THREAD CREATE tZ(x&) TO hThread???

THREAD CLOSE hThread??? TO lResult&

NEXT x&

'Wait until the threads are all done

DO

SLEEP 100

LOOP WHILE THREADCOUNT > 1

END FUNCTION


THREADED statement

Purpose

Declare thread-local variables.

Syntax

THREADED variable[()] [AS type] [, variable[()]]

THREADED variable[()] [, variable[()]] [, ...] AS type

Remarks

Threaded variables are global to every Sub and Function but are not shared across threads. Each thread has its own independent set of thread-local variables.

To declare an array as a threaded variable, use an empty set of parentheses in the variable list: You can then use the DIM statement to dimension the array.

THREADED MyArray%()

THREADED StringArray() AS STRING

The THREADED statement may, optionally, accept a list of variables, all of which are defined by the type descriptor keyword that follows them. For example:

THREADED aaa, bbb, ccc AS INTEGER

THREADED vptr, aptr() AS LONG PTR

Restrictions

DEFtype has no effect on variables defined by a .

See also

DIM, GLOBAL, LOCAL, STATIC, THREADED variables

Example

THREADED xxx, yyy, zzz AS INTEGER

THREADED vptr, aptr() AS LONG PTR


THREADID function

Purpose | Return a Long-integer thread identifier of the current thread.
Syntax | thrdID& = THREADID
Remarks | The thread ID value is returned for the thread that is currently executing. The Thread ID is intended for use with the various (advanced) thread-related API functions provided by Windows.
Restrictions | The thread ID value cannot be used interchangeably with the thread handle returned by THREAD CREATE.
See also | FUNCTION/END FUNCTION, THREAD CLOSE, THREAD CREATE, THREAD RESUME, THREAD STATUS, THREAD SUSPEND, THREADED variables


TIME$ system variable

Purpose

Read and/or set the system time.

Syntax

To read the time:

s$ = TIME$

To set the time:

TIME$ = string_expression

Remarks

The system variable TIME$ contains an eight-character that represents the time of the system clock in the form "hh:mm:ss", where hh is hours (in 24-hour military form), mm is minutes, and ss is seconds.

Assigning string_expression to TIME$ resets the system clock. string_expression must contain time information in military (24-hour) format. Minutes and seconds information can be omitted. For example:

TIME$ = "12" 'set clock to 12 noon

TIME$ = "13:01" 'set clock to 1:01 PM

TIME$ = "13:01:30" 'set clock to 30 sec after 1:01 PM

TIME$ = "0:01" 'set clock to 1 min after midnight

Use the TIMER function to return the number of seconds that have elapsed since midnight (or since the system was booted).

See also

DATE$, TIMER


TIMER function

Purpose | Return the number of seconds that have elapsed since midnight.
Syntax | y = TIMER
Remarks | TIMER returns the number of seconds since midnight as a Double-precision floating-point value. The resolution is about 1/100 of a second on NT-based platforms, or 1/18th of a second on earlier platforms.
See also | DATE$, TIME$
Example | OldTime$ = TIME$ ' Current time TIME$ = "12" ' Noon NoonSec$ = FORMAT$(TIMER, "#,") x$ = "Noon is " + NoonSec$ + " seconds past midnight" TIME$ = OldTime$ ' Restore time
Result | Noon is 43,200 seconds past midnight


TRACE statement

Purpose

Capture a representation of the precise flow of execution in a module.

Syntax

TRACE NEW fname$

TRACE ON

TRACE PRINT string_expr

TRACE OFF

TRACE CLOSE

Remarks

The TRACE statement is used to generate a trace file detailing program flow as execution passes through Labels, plus entry and exit of all Subs and Functions, along with details of passed parameters, etc. All trace details are written to a named disk file fname$.

TRACE also logs PowerBASIC run-time errors as they occur, to assist with locating program errors. TRACE can be dynamically started and stopped with the TRACE ON and TRACE OFF statements to enable the programmer to check specific portions of a program without generating volumes of irrelevant trace data.

The five general forms of the TRACE statement are described as follow:

TRACE NEW fname$

TRACE NEW causes a standard sequential trace file (of the specified file name fname$) to be created, deleting any previous file of the same name.

TRACE ON

When a subsequent TRACE ON is then executed, PowerBASIC begins to write pertinent trace information to the trace file. It will contain a chronological list of every call to an internal Sub/Function, the associated parameter values, and the point at which it was exited. Further, it will list a label name each time that program execution flows through the label position.

In a test or debugging situation, TRACE, CALLSTK, and CALLSTK$ allow you to easily answer that age-old programming question, "How did I get here?". TRACE details the entry and exit of every Sub or Function in your program, while CALLSTK simply lists the stack frames that exist above the current level. TRACE is particularly valuable in pinpointing the area of a program where a fatal machine crash occurs.

TRACE PRINT string_expr

TRACE PRINT writes the value of string_expr to the trace file. It can be used to record the value of important variables or other information of importance.

TRACE OFF

TRACE OFF temporarily stops output to the trace file. The trace can be subsequently restarted with another TRACE ON statement. An implied TRACE OFF is performed when you exit the Sub or Function in which the current TRACE ON was executed.

TRACE CLOSE

TRACE CLOSE permanently detaches the trace file from the stream of trace data.

The TRACE statement can easily create a huge trace file, so caution must be exercised. Use TRACE ON at the lowest Sub or Function level possible, to keep the output size within reason.

If PBMAIN contains TRACE NEW and TRACE ON statements, and subsequently calls SUB AAA(x&), which in turn calls SUB BBB(y&,a$), which then calls SUB CCC(z&), which encounters a run-time error 5, the trace file might look something like this:

Trace Begins...

AAA(3)

BBB(4,string data)

CCC(5)

TRACE PRINT printed this user data from CCC()

ERROR 151 was generated in this thread

CCC Exit

BBB Exit

AAA Exit

Numeric parameters are displayed in decimal, while pointer and array parameters display a decimal representation of the offset of the target value.

Restrictions

TRACE can be invaluable during debugging, but it generates substantial additional code that should be avoided in the final release version of an application. If the source code contains #TOOLS OFF, all TRACE statements which remain in the program are ignored by the compiler, and the parameters and expressions are excluded from the compiled program.

To conserve memory requirements in the code, long labels are truncated to 13 characters; however, Sub and Function names are not truncated.

The TRACE statement is "Thread-Aware", displaying only Sub, Function, or Label details from the thread in which it was executed. You can execute TRACE multiple times, or even in multiple concurrent threads. However, you must use caution to ensure that each thread uses a unique name for its own trace file.

See also

#TOOLS, CALLSTK, CALLSTK$, CALLSTKCOUNT, FUNCNAME$, PROFILE

Example

#TOOLS ON

FUNCTION PBMAIN

TRACE NEW "tracelog.txt"

TRACE ON

x& = 3

CALL AAA(x&)

TRACE OFF

TRACE CLOSE

END FUNCTION

SUB AAA(x&)

INCR x&

CALL BBB(x&,"string data")

' More code

END SUB

SUB BBB(y&,a$)

INCR y&

CALL CCC(y&)

END SUB

SUB CCC(z&)

TRACE PRINT "TRACE PRINT printed this " + _

"user data from " + FUNCNAME$ + "()"

ERROR 151 ' Trigger a run-time error

END SUB


TRIM$ function

Purpose

Return a copy of a with leading and trailing characters or substrings removed.

Syntax

NewString$ = TRIM$(OldString$ [, [ANY] CharsToTrim$])

Remarks

TRIM$ combines the functionality of LTRIM$ and RTRIM$ into a single function. OldString$ is the string expression from which to remove characters, and CharsToTrim$ is the string expression to remove leading and trailing occurrences. If CharsToTrim$ is not specified, TRIM$ removes leading and trailing spaces.

ANY

If the ANY keyword is included, CharsToTrim$ specifies a list of single characters to be searched for individually, a match on any one of which as a leading or trailing character will cause the character to be removed from the result.

Restrictions

TRIM$ is case sensitive, so capitalization matters. TRIM$ is often used to remove the leading space returned by a STR$ conversion. While this method does work perfectly, it is a common misuse simply because STR$ will never return any trailing space characters. On this basis, the use of LTRIM$(STR$(x&)) is more efficient than TRIM$(STR$(x&)). Alternatively, FORMAT$(x&) may be used, since trimming of space characters is unnecessary.

See also

FORMAT$, INSTR, LCASE$, LTRIM$, MCASE$, MID$, REMOVE$, REPLACE, RIGHT$, RTRIM$, TALLY, UCASE$, VERIFY


TRY/END TRY block

Purpose

A structured method of trapping and responding to run-time errors.

Syntax

TRY

{statements}

[EXIT TRY]

{statements}

CATCH

{error handling statements}

[EXIT TRY]

{error handling statements}

[FINALLY

{statements}

[EXIT TRY]

{statements}]

END TRY

Remarks

Statements in the TRY section are executed normally. The first time a run-time error occurs, control is transferred to the CATCH section. If no run-time errors are generated in the TRY section, the CATCH section is skipped entirely.

Then, regardless of error status, the FINALLY section is executed, if it is present. Error trapping and control transfer are disabled in the CATCH and FINALLY sections, so you would normally use conventional " ERR =…" tests to check the success of error-prone operations in those sections.

However, TRY structures can be nested to any level, so it may be desirable to use another TRY block within these clauses.

Restrictions

CATCH is a mandatory section of this structure, although the FINALLY section is optional.

Because of the nesting requirements, the ERR value is local to the TRY structure. Upon exit, the prior ERR value is restored, so be sure to save the value of ERR if it will be needed outside of the TRY structure.

To leave the TRY structure, execution must pass normally through END TRY, or by an EXIT TRY statement. Leaving a TRY block any other way is strongly discouraged because error trapping will remain disabled, and the previous ERR value will not be restored. Future versions of PowerBASIC may disallow such practices.

ON ERROR GOTO is invalid within a TRY structure, but may be used within the same Sub/Function.

See also

#DEBUG ERROR, ERL, ERR, ERRCLEAR, ERROR, Error Overview, ERROR$, Error Trapping, ON ERROR, Run-time Errors

Example

TRY

OPEN "file.dat" FOR INPUT LOCK READ WRITE AS #1

CATCH

CALL NotifyUserOfError(ERR)

EXIT TRY

FINALLY

CALL UpdateDataBase()

CLOSE #1

END TRY


TYPE/END TYPE block

Purpose

Define a User-Defined Data Type (UDT), containing one or more member elements.

Syntax

TYPE MyType [BYTE | WORD | DWORD | QWORD] [FILL]

[MemberName [(subscripts)] AS] TypeName

[MemberName [(subscripts)] AS TypeName]

[...]

END TYPE

Remarks

The TYPE statement has the following parts:

TYPE

The beginning of a User-Defined Type definition.

MyType

The name of the User-Defined Type, which must conform to standard variable naming conventions.

Member alignment

TYPE definitions may optionally specify an alignment of BYTE (the default), WORD, DWORD, or QWORD, as well as FILL characteristics. With standard alignment, each member of a Type Structure will be located on the specified boundary. For example, with DWORD, up to 3 bytes may be skipped between members to accomplish alignment.

However, when a user-defined type is defined as a member of a larger user-defined type, this "sub-type" retains its original size and alignment, just as first declared.

BYTE

Each member will be aligned on a BYTE boundary - no padding or alignment is applied to the structure. This is the default alignment method.

WORD

Each member will be aligned on a WORD boundary. Any odd byte between members of TYPE will be automatically skipped and ignored. The UDT structure may also be padded with one trailing byte to ensure the total structure size is a multiple of 2 bytes.

DWORD

Each member will be aligned on a DWORD boundary. Up to three bytes will be skipped to accomplish this alignment. The UDT structure is also padded with up to three trailing bytes to ensure the total structure size is a multiple of 4 bytes.

QWORD

QWORD alignment is included for compatibility with Windows, it cannot be fully implemented in a 32-bit operating system. With QWORD, individual members are 64-bit aligned for the appropriate structure size, but variables of that type may only be aligned on 32-bit boundaries, as stack pointer alignment is not guaranteed.

FILL

If the FILL option is specified, such as TYPE xxx DWORD FILL, the following rules apply:

1. No bytes are skipped if the next member of the Type will fit entirely into that space to be skipped.

2. Fixed-length strings are considered to be an array of bytes, so no bytes are skipped preceding them.

3. The total size of an array is considered to determine if FILL should affect its placement within the structure. For example, with DWORD FILL, an array of two integers would be started on a 4-byte boundary, even if two or three bytes must be skipped.

Microsoft Visual Basic uses an alignment style equivalent to DWORD FILL.

Type members

MemberName

The name of a member of the User-Defined Type. This too must follow the standard variable naming conventions.

subscripts

The dimensions of a member array. Arrays of one and two dimensions are supported, but must be defined with constant or numeric literal values. That is, the total size of a UDT must be known at compile-time, so items like dynamic strings, which vary in size, cannot be part of a TYPE structure. A STRING PTR can, however, since a pointer is implemented as a DWORD.

Like conventional arrays, the default lower array boundary is zero, but positive non-zero values may be used to specify a specific range of subscript index values for the array, separated from the upper array boundary subscript with the TO keyword. Additionally, both the lower and upper subscript index values must be zero or greater (ie, negative subscript values are not permitted). Examples of valid syntax follow:

TYPE MYTYPE

id AS INTEGER ' Scalar UDT member

Styles(6) AS DWORD ' 7 elements (0 TO 6)

Yrs(1980 TO 2010) AS LONG ' 31 elements

Team(100 TO 101) AS BYTE ' 2 elements

Rating(1 TO 10) AS DWORD ' 10 elements

X(1 TO 5, 0 TO 5) AS EXT ' 30 elements (5x6)

Y(4,3) AS QUAD ' 20 elements (5x4)

END TYPE

Individual UDT structures can be up to 16 MB each. A single member element of a UDT may also occupy the entire 16 MB. For example, arrays within a UDT, ASCIIZ strings, and fixed-length strings. UDT member arrays are not resizable at runtime. Additionally, the cannot be used directly on a UDT member array. Instead, use DIM..AT to declare a conventional array at the same memory address as the UDT member array, and the ARRAY statement can then be used on that array.

TypeName

One of the supported data types, including User-Defined Types and Unions, with the exception of arrays.

END TYPE

Marks the end of the User-Defined Type definition.

It is often very convenient to be able to refer to several different types of things as a single unit or data structure. For example, in an accounting program, an account number and amount are part of what makes up a single journal entry. The TYPE/END TYPE block statements make it easy to create a single UDT that holds such information.

TYPE JournalType DWORD ' type name and alignment

AccountNumber AS LONG ' member name and data type

Amount AS CUR ' this is another one

END TYPE ' end of type declaration

DIM JournalEntry AS JournalType ' declare a record

TYPE/END TYPE blocks must be defined outside of a Sub or Function and may be defined only once in any program. It is usually easiest to put your TYPE/END TYPE block definitions in an Include file and use the #INCLUDE metastatement in any module that may need to use them.

TYPE/END TYPE blocks do not declare any variables; instead, they simply define a new type. You can declare variables of that type using the DIM or REDIM statements, or any statement that lets you use an AS clause:

DIM TypeVariable as TypeVariableType

Once you have a User-Defined Type variable declared, you can access its member elements using the following format:

TypeVariable.Element

For example, to change the account number in the JournalEntryType type, you might use a statement like:

JournalEntry.AccountNumber = 1000

A User-Defined Type can be used like any built-in PowerBASIC type. For example, you can define an array of record variables:

DIM JournalEntries(1 TO 100) AS JournalEntryType

…or even create a procedure that accepts a record variable:

SUB PrintJournalEntry(aJournalEntry AS JournalEntryType)

...

END SUB

You can also use pointers in a TYPE definition. Note that the first member in the next example is auto-aligned to start on a DWORD boundary, and three bytes are skipped so that the second member is also aligned on a DWORD boundary:

TYPE MyType DWORD

Count AS BYTE ' Aligned to a DWORD boundary

y AS INTEGER PTR ' Aligned to next DWORD boundary

z AS STRING PTR

END TYPE

Since pointers are stored as a DWORD, their length is 4 bytes when used in a TYPE/END TYPE, regardless of the length of their target. To access the target of a pointer, you must place the at-sign in front of the TYPE/END TYPE member, not the name of the TYPE itself:

iResult% = @MyType.y ' Invalid

iResult% = MyType.@y ' Valid

You can also declare a variable that is a pointer to a TYPE:

TYPE MyData

Val1 AS INTEGER

Val2 AS INTEGER

Val3 AS INTEGER

Val4 AS INTEGER

END TYPE

DIM Info AS MyData PTR

Info = VARPTR(YourData)

Message$ = HEX$(@Info.Val1) + $CRLF + _

HEX$(@Info.Val2) + $CRLF + _

HEX$(@Info.Val3) + $CRLF + _

HEX$(@Info.Val4)

Note that the target specifier is in front of the TYPE name since it is the pointer. Val1, Val2, Val3, and Val4 represent offsets from that pointer. PowerBASIC does support a pointer within a structure pointer, but you should be very careful in their use. Changing the structure pointer itself could make all member pointers invalid. See the topic on pointers for more information.

Bit Variables

TYPE structures may contain bit variables, which are named BIT (unsigned values) or SBIT (signed values). Each bit variable may occupy from 1 to 31 bits, and they may be packed one after another up to a total of 32 bits per bit field. The size of a bit variable is defined as follows:

var AS BIT * nlit [IN BYTE|WORD|DWORD]

…where the term "* nlit" defines the number of bits (1 to 31), and the optional term "IN BYTE|WORD|DWORD", if present, defines the start of a new bit field of 1, 2, or 4 bytes. For example:

TYPE ABCD

Valu2 AS BIT * 31 IN DWORD

Sign1 AS SBIT * 1

nybl2 AS BIT * 4 IN BYTE

nybl1 AS BIT * 4

END TYPE

The example TYPE structure above is 5 bytes in size, containing a 4-byte bit field and a 1-byte bit field. In this case, each contains two bit-variables of varying size. The range of values which may be stored depends upon the number of bits available. For example, "BIT * 4" has a range of 0 to 15, "SBIT * 1" has a range of -1 to 0, and "SBIT * 5" has a range of -16 to +15.

Structures within structures

Structures (TYPE/UNION) may be embedded within another structure, for simplification in referencing deeply nested items, by simply stating the structure name alone at the appropriate position. The internal alignment of the member structure is precisely maintained regardless of other alignment specifications, to foster inheritance issues. For example:

TYPE ABCD3

A AS LONG

ABCD2

C AS LONG

END TYPE

TYPE ABCD2

D AS DWORD

E AS DOUBLE

ABCD1

END TYPE

UNION ABCD1

F AS DWORD

G AS LONG

H AS SINGLE

END UNION

In this case, you could access the lone Single-precision float member of this structure very simply. Assuming DIM X AS ABCD3, you could reference the Single-precision Union member with the statement X.H, instead of the extended syntax X.ABCD2.ABCD1.H

For related information, please refer to the UNION/END UNION and User-Defined Types and Unions sections.

Restrictions

When measuring the size of a padded (aligned) UDT structure with the LEN or SIZEOF statements, the measured length includes any padding that was added to the structure. For example, the following UDT structure:

TYPE LengthTestType DWORD

a AS INTEGER

END TYPE

...

DIM abc AS LengthTestType

x& = LEN(abc)

Returns a length of 4 bytes in x&, since the UDT was padded with 2 additional bytes to enforce DWORD alignment. Note that the LEN and SIZEOF of individual UDT members will return the true size of the member without regard to padding or alignment. In the previous example, LEN(abc.a) returns 2.

Individual UDT structures can be up to 16 MB each. Arrays within a UDT, ASCIIZ strings and fixed-length strings may occupy the full 16 MB structure size limit.

Field strings and dynamic strings cannot be used in UDT or UNION structures. Attempting to do so results in a compile-time Error 485 ("Dynamic/Field strings not allowed").

See also

DIM, LEN, REDIM, SIZEOF, TYPE SET, UNION/END UNION, User-Defined Types, Unions

Example

TYPE JournalEntryType

AccountName AS STRING * 20

AccountNumber AS LONG

Amount AS CUR

END TYPE

DIM JournalEntry AS JournalEntryType

JournalEntry.AccountName = "Joe Smith"

JournalEntry.AccountNumber = 7467047&

JournalEntry.Amount = 42.01@

' process journal entry here

...

JournalEntry.AccountNumber = 705233476&

JournalEntry.Amount = 69.35@

' process journal entry here

...


TYPE SET statement

Purpose

Assign the value of a User-Defined Type or string expression into another User-Defined Type or variable.

Syntax

TYPE SET mainvar = {typevar | stringexpr$} [USING ustring_expression]

Remarks

TYPE SET is primarily designed to assign the value of a User-Defined Type (UDT) to a different class of User-Defined Type. Additionally, TYPE SET can be used to assign a string expression (stringexpr$) to a UDT or a string variable.

USING

Character positions remaining after the assignment are filled (padded) in mainvar as directed by the optional USING clause.

If the assigned data is shorter than the string or type variable mainvar, it is padded on the right with the first character of the USING string expression ustring_expression, or binary zeros ($NUL) if not specified.

If the USING string is null (zero length or empty), any trailing padding character positions are left unchanged in mainvar. This is similar to the ABS option in the LSET statement. To perform right-justification, use the RSET statement.

See also

CSET, CSET$, LSET, LSET$, RSET, RSET$, TYPE/END TYPE

Example

TYPE udt1

x AS STRING * 12

y AS LONG

z AS INTEGER

END TYPE

TYPE udt2

a(1 TO 18) AS BYTE

END TYPE

FUNCTION PBMAIN

DIM u1 AS udt1

DIM u2 AS udt2

u1.x = "ABC"

TYPE SET u2 = u1

a$ = CHR$(u2.a(1), u2.a(2), u2.a(3))

TYPE SET u2 = "1" USING "2"

b$ = CHR$(u2.a(1), u2.a(2), u2.a(3))

END FUNCTION

Result

a$ contains "ABC"

b$ contains "122"


UBOUND function

Purpose

Return the largest possible subscript (boundary) for an array's specified dimension.

Syntax

y = UBOUND(array [(dimension)])

y = UBOUND(array, dimension)

Remarks

array is the array of interest. dimension is an value or expression from 1 up to the number of dimensions in array; it specifies which dimension's upper bound value will be returned. If you omit dimension, it defaults to 1 (the first dimension). To find the lower bound of an array's dimension, use the LBOUND function. Use LBOUND and UBOUND together to determine an array's size. UBOUND of an undimensioned array returns -1, so that UBOUND(array) - LBOUND(array) + 1 yields zero (0) for such an array.

Restrictions

UBOUND cannot be used on arrays within User-Defined Types.

See also

ARRAYATTR, DIM, LBOUND, REDIM

Example

' Dimension an array with lower and upper bounds

DIM MyArray%(1900 TO 2000,5 TO 10)

' print out the values of the array

Message$ = "The array's first dimension is from" + _

STR$(LBOUND(MyArray%(1))) + "to" + _

STR$(UBOUND(MyArray%(1)))

Message$ = "The array's second dimension is from" + _

STR$(LBOUND(MyArray%(2))) + "to" + _

STR$(UBOUND(MyArray%(2)))

Result

The array's first dimension is from 1900 to 2000

The array's second dimension is from 5 to 10


UCASE$ function

Purpose

Return an all-uppercase (capitalized) version of a .

Syntax

s$ = UCASE$(string_expression [,ANSI | OEM])

Remarks

UCASE$ returns a string equivalent to string_expression, except that all the lowercase alphabetic characters in string_expression are converted to uppercase. The optional ANSI or OEM parameter specifies whether string_expression is converted to uppercase using the ANSI or OEM charset. If no charset option is selected, then the Console’s current OEM charset is used to convert string_expression to lowercase. The PowerBASIC Compiler for Windows defaults to using the systems ANSI charset to convert string_expression. Only "International" characters in the range CHR$(128) to CHR$(255) are affected by the charset parameter.

See also

ASC, LCASE$, MCASE$

Example

x$ = UCASE$("Beware of cats!")

Result

BEWARE OF CATS!


UCODE$ function

Purpose

Translate an ANSI into a Unicode string.

Syntax

a$ = UCODE$(AnsiStrExpression)

Remarks

UCODE$ returns the Unicode, multi-byte equivalent string of the ANSI string contained in AnsiStrExpression. To convert a Unicode string into ANSI, use the ACODE$ function.

Unicode strings require two bytes to represent a Unicode character, whereas ANSI strings (the native PowerBASIC string format) uses one byte to represent a character. Therefore, UCODE$ returns a string that has twice the byte count of the ANSI string, yet represents the same number of characters.

See also

ACODE$


UDP CLOSE statement

Purpose | Close a previously opened UDP socket that was created with the UDP OPEN statement.
Syntax | UDP CLOSE [#] fNum&
Remarks | Close the previously opened UDP/IP port specified by fNum&.
See also | TCP and UDP Communication, TCP CLOSE, UDP NOTIFY, UDP OPEN, UDP RECV, UDP SEND


UDP NOTIFY statement

Purpose

Designate which UDP/IP events will generate a notification message.

Syntax

UDP NOTIFY [#] fNum&, {SEND | RECV | CLOSE} TO hWnd& AS wMsg&

Remarks

Designates which events (SEND, RECV, and CLOSE) will generate a notification wMsg& message, to be sent to the window/dialog procedure (CALLBACK), identified by the window handle hWnd&.

Your program defines the wMsg& value, and this value should be equal or larger than %WM_USER + 500, to avoid conflict with common Windows callback message values.

When the nominated Callback Function receives the wMsg& notification, the wParam& parameter identifies the operating system's handle of the socket (see FILEATTR). The low-order Word of lParam& specifies the code of the event (see table below), and the high-order Word of lParam& contains the error code (if any).

LO(WORD, lParam&) | Definition
%FD_READ | Data is available to be read from the socket.
%FD_WRITE | The socket is ready for data to be written.
%FD_CLOSE | The socket has been closed.

Notification messages do not arrive in unabated or continuous streams. That is, once a particular notification message arrives, it will not be sent again until the initial message is acted upon. For example, if an %FD_READ notification is received for a particular socket, it will not be resent until after a UDP RECV statement is executed.

The Winsock error codes are listed in WS2_32.INC, prefixed with %WSAE.

See also

FILEATTR, TCP and UDP Communication, TCP NOTIFY, UDP CLOSE, UDP OPEN, UDP RECV, UDP SEND


UDP OPEN statement

Purpose | Create a socket for an application to communicate with a UDP server or client using the UDP (connectionless) protocol over Winsock (UDP/IP).
Syntax | UDP OPEN [PORT p&] AS [#] fNum& [TIMEOUT timeoutval&]
Remarks | Open a UDP socket (port or service) for UDP communication. FNum& is a file number such as #1, or a variable with a value obtained using the FREEFILE function.
PORT | If PORT p& is specified, the socket is opened as a server that can receive UDP data. Use the UDP NOTIFY statement to receive server notifications from the socket so that the data can be retrieved. Common port numbers include 7 (Echo, see RFC862); 37 (Time, see RFC868); and 123 (NTP - RFC1305).
TIMEOUT | The TIMEOUT option allows you to specify how long, in milliseconds (mSec), a UDP SEND/RECV operation should wait for completion. If the specified number of milliseconds elapses, the UDP operation will fail, and the ERR system variable will be set to indicate a run-time Error 24 ("Device timeout"). The default timeout is 60000 milliseconds (60 seconds).
See also | TCP and UDP Communication, FREEFILE, TCP OPEN, UDP CLOSE, UDP NOTIFY, UDP RECV, UDP SEND


UDP RECV statement

Purpose

Receive data from a previously opened UDP port.

Syntax

UDP RECV [#] fNum&, FROM ip&, pNum&, Buffer$

Remarks

Receive any bytes from the previously opened UDP port specified by fNum&, and place them into Buffer$. The IP address that sent the UDP packet is placed into the ip& variable, and the port number is placed into the pNum& variable.

ip& and pNum& may be subsequently used to send data back in response to data received.

UDP RECV is a blocking statement. That is, execution does not continue until either data is retrieved from the socket, or the timeout period expires.

If a timeout occurs, a run-time Error 24 ("Device timeout") is generated and placed in the ERR system variable. See UDP OPEN to specify the UDP socket timeout value.

See also

TCP and UDP Communication, TCP RECV, UDP CLOSE, UDP NOTIFY, UDP OPEN, UDP SEND


UDP SEND statement

Purpose

Send a of data through a previously opened UDP socket.

Syntax

UDP SEND [#] fNum&, AT ip&, pNum&, string_expression

Remarks

Write the specified string_expression to the UDP/IP port pNum& at the IP address specified in ip&, using the UDP connection specified by fNum&.

See also

TCP and UDP Communication, TCP SEND, UDP CLOSE, UDP NOTIFY, UDP OPEN, UDP RECV


UNION/END UNION statements

Purpose

Create a new User-Defined Type definition whose member elements overlap in memory.

Syntax

UNION UnionName

MemberName [(subscripts)] AS TypeName

[MemberName [(subscripts)] AS TypeName]

[...]

END UNION

Remarks

A union is a type - very similar to a User-Defined Type - except that its elements overlap in memory. While this may seem strange at first, it has enormous potential.

For example, say you are designing an accounting program. You want to make it general purpose so it has widespread appeal. But everyone does their accounting differently; for example, some people use account numbers that are plain Integers, while others may use alphanumeric account names. Using a Union makes this easy. Another common use of a Union is variable type conversion. The is best described by way of an example:

UNION VarConvert

iLong AS LONG

iDword AS DWORD

sStr AS STRING * 4

END UNION

DIM x AS VarConvert, y AS DWORD, z AS STRING

x.iLong = 123456&

y = x.iDword

z = x.sStr

Like a User-Defined Type, a Union may also contain arrays, and these follow the same rules as User-Defined Type member arrays (see Type Members for syntax rules and additional examples). The following example demonstrates the use of a Union member array:

UNION Arrs

a1(1 TO 1024) AS BYTE

st AS ASCIIZ * 10

END UNION

FUNCTION PBMAIN

DIM a AS Arrs

a.a1(1) = 72

a.a1(2) = 101

a.a1(3) = 108

a.a1(4) = 108

a.a1(5) = 111

a.a1(6) = 33

' At this point, a.st contains "Hello!"

END FUNCTION

Bit Variables

UNION structures may contain bit variables, which are named BIT (unsigned values) or SBIT (signed values). Each bit variable may occupy from 1 to 31 bits, and they may be packed one after another up to a total of 32 bits per bit field. The size of a bit variable is defined as follows:

var AS BIT * nlit [IN BYTE|WORD|DWORD]

…where the term "* nlit" defines the number of bits (1 to 31), and the optional term "IN BYTE|WORD|DWORD", if present, defines the start of a new bit field of 1, 2, or 4 bytes. For example:

UNION ABCDE

Odd1 AS BIT * 1 IN DWORD

Value1 AS LONG

END UNION

The example UNION structure above is 4 bytes in size, containing a 1-byte bit field and a 4-byte LONG.

UNION abcde

Part1 AS BIT * 8 IN DWORD

Part2 AS BIT * 16

END UNION

The example union above is 4 bytes in size, containing an 8-bit field and an overlapping 16-bit field.

Structures within structures

Structures (TYPE/UNION) may be embedded within another structure, for simplification in referencing deeply nested items, by simply stating the structure name alone at the appropriate position. The internal alignment of the member structure is precisely maintained regardless of other alignment specifications, to foster inheritance issues. For example:

TYPE ABCD3

A AS LONG

ABCD2

C AS LONG

END TYPE

TYPE ABCD2

D AS DWORD

E AS DOUBLE

ABCD1

END TYPE

UNION ABCD1

F AS DWORD

G AS LONG

H AS SINGLE

END UNION

In this case, you could access the lone Single-precision float member of this structure very simply. Assuming DIM X AS ABCD3, you could reference the Single-precision Union member with the variable name X.H, instead of the extended syntax X.ABCD2.ABCD1.H

Restrictions

A Union can contain elements of dissimilar sizes. The size of a Union structure is always determined by the longest member element. This is usually an important consideration when using a Union within another Union or UDT structure, in order to determine the size of the final structure.

For related information, please refer to the TYPE/END TYPE, User-Defined Types and Unions sections.

Field strings cannot be used in UDT or UNION structures. Attempting to do so results in a compile-time Error 485 ("Dynamic/Field strings not allowed").

See also

DIM, LEN, SIZEOF, TYPE/END TYPE, User-Defined Types, Unions

Example

UNION AccountUnion

AccountNumber AS LONG

AccountName AS STRING * 16

END UNION

TYPE JournalEntryType

Account AS AccountUnion

Amount AS CUR

END TYPE

DIM JournalEntry AS JournalEntryType

JournalEntry.Account.AccountName = "Smith"

JournalEntry.Amount = 123.01@

...

JournalEntry.Account.AccountNumber = 1001

JournalEntry.Amount = -1.99@


UNLOCK statement

Purpose

Remove locks placed on a file to permit other threads, processes, and applications to access the locked sections of the file.

Syntax

UNLOCK [#] filenum& [, {record&& | start&& TO end&&}]

Remarks

UNLOCK restores access to a record, range of records, byte, or range of bytes locked by the LOCK statement, in file opened as file number filenum&.

If the file was opened in random-access mode, record&&, start&&, and end&& specify record numbers.

When used with binary mode files, record&&, start&&, and end&& specify byte positions, starting from either one (the default) or zero, depending on the BASE setting given when the file was Opened.

If a record is specified, only that record (or byte) is unlocked. Otherwise, a range of records (or bytes) is unlocked, from start&& to end&&. If no records are specified, or if the file was opened in sequential mode, the entire file is unlocked.

All records (or bytes) to be unlocked must have been previously locked using the LOCK statement. Multiple locks may be placed on a file, and locks may be unlocked in any order. However, the parameters used for each UNLOCK statement must exactly match those used for the previous corresponding LOCK statement.

All locked records (or bytes) must be unlocked using the UNLOCK statement before the file can be closed.

If an unlock attempt fails, PowerBASIC sets the ERR system variable to reflect a run-time Error 70 ("Permission denied"), or Error 75 ("Path/file access error").

See also

LOCK, OPEN

Example

See the example for LOCK.


USING$ function

Purpose

Format one or more or expressions, based upon the contents of the format mask string.

Syntax

sResult$ = USING$(fmtmask$, expr [, expr [, ...]])

Remarks

The rules of formatting are based upon the PRINT USING statement supported in many DOS versions of BASIC, including PowerBASIC for DOS.

However, since it is implemented as a function, it allows far more versatility in that it is not necessary to output a value to gain the benefit of this unique functionality. Also, USING$ offers a wider range of applications than FORMAT$ because it can format both numeric and string expressions, and can take multiple arguments.

fmtmask$

A string expression, string variable or string literal consisting of format characters that will determine how the complete expression should be formatted. This expression is termed the mask. There may be as many format masks in fmtmask$, arranged in the same order as the expr arguments are specified. See the examples below for more information.

expr

A string or numeric expression, variable, or literal value to be formatted. The mask characters available depend on whether expr is a string or numeric.

Character | Definition
(string expr) | When expr is a string, the following format codes apply within fmtmask$:
! | The first character of the string is returned.
& | The entire string is returned.
\\ | The first two characters are returned.
\ \ | If backslashes enclose n spaces, n + 2 characters of the string expression are returned.
_ | Escape (underscore) character. The following character is interpreted as a literal character instead of a mask format character.
(numeric expr) | When expr is numeric, the following format codes apply within fmtmask$:
# | A numeric digit position, which is space-filled to the left, and zero-filled to the right of the decimal point. If the number is negative, a minus sign occupies a digit position.
. | The decimal point is placed at this position.
, | A numeric digit position, which signifies that whole number digits should be displayed with a comma each three digits.
$$ | Two numeric digit positions which cause a dollar sign to be inserted immediately before the number.
*x | Two numeric digit positions which cause leading blank spaces in the field to be replaced with the character in the second position of the pair "x" (where "x" represents your own choice of character). For example, two asterisks "**" will convert leading spaces to asterisks, and "*=" converts leading spaces to equals characters, etc. The *x mask characters also act as two digit (#) placeholders. Your mask must contain at least three characters to use this.
+ | A plus at the start of the field causes the sign of the value (+ -) to be inserted before the number. A plus at the end of the field causes the sign of the value (+ -) to be added after the number.
- | A minus at the end of the field causes a minus signed to be added after a negative number, or a space to be added after a positive number. A minus at the start of the field is treated as a literal character, which is always inserted.
^ | Numbers can be formatted in scientific notation by including three to six carets (^) in the format string. Each caret corresponds to a numeric digit position in the exponent, one for E, one for the exponent sign, and one to four for the actual digits of the exponent value.
_ | Escape (underscore) character. The following character is interpreted as a literal character instead of a mask format character. Therefore, to include a literal underscore character in the format mask, use two underscore characters.
 | All characters in the format mask string that are not identified above are copied into the output string just as they are encountered. You can override or escape any special format code by preceding it with an underscore character ( _ ) and it will be copied as any other literal character. This provides the flexibility to include literal string text within the formatted return string.

Restrictions

The returned string is limited to an absolute length limit of 1024 bytes.

By specifying a single mask in fmtmask$, all expr arguments are subjected to the single mask. See the examples below.

If there are fewer expr arguments than matching format masks in fmtmask$, parsing of the fmtmask$ halts after the last referenced mask position, and subsequent characters in fmtmask$ are ignored. This is consistent with the behavior of PRINT USING$ in PB/DOS.

If a numeric argument overflows its mask (i.e., there are more digits than digit positions), the resulting string will occupy as many spaces as needed to represent the number. In such cases, PB/DOS includes a leading "%" symbol to indicate the mask overflow; however, PowerBASIC for Windows does not return the additional "%" overflow character.

The semicolon (;) and zero (0) characters are reserved for future use, so it would be prudent to escape such literal characters in USING$ masks to maintain future compatibility.

See also

FORMAT$, STR$

Example

a$ = USING$("!", "abc")

' returns "a"

a$ = USING$("You owe $$#,.##", 12345.67@)

' returns "You owe $12,345.67

DIM p AS BYTE PTR

HOST ADDR "localhost" TO ip&

p = VARPTR(ip&)

a$ = USING$("#_.#_.#_.#", @p, @p[1], @p[2], @p[3])

' returns "127.0.0.1"

a$ = USING$("&=#.##############", "Pi", ATN(1)*4)

' returns "Pi=3.14159265358979"

a$ = USING$("!", "AX", "BX", "CX")

' returns "ABC"

a$ = USING$("$#.##_,", 1,20,300,4)

' returns "$1.00,$20.00,$300.00,$4.00,"

a$ = USING$("$*=#####.##_,",1,20)

' returns "$======1.00,$=====20.00,"


VAL function

Purpose

Return the equivalent of a argument.

Syntax

y = VAL(string_expression)

Remarks

VAL turns a string argument into a number. If string_expression begins with numeric characters (0 to 9 and +, -, or.), but also contains non-numeric characters, VAL returns the number up to the point of the non-numeric character. If string_expression does not begin with a numeric character, VAL returns 0. The string argument should not contain any commas, as VAL terminates processing if a comma is encountered. Leading white-space characters (spaces, tabs, and linefeed characters) are ignored.

VAL is often used in data entry routines. With it, a program can prompt the user for numeric data (which is usually retrieved in string form), and then convert the legal portions of the string into a numeric result (see the examples below).

VAL also interprets the letters "e" and "d" (and "E" and "D") as the symbols for exponentiation. Both "e" and "d" are supported for compatibility with other BASIC languages, but there is no difference in operation between these symbols. For example:

i& = VAL("10.101e3") ' 10101 ~ 10.101*(10^3)

j& = VAL("2D4") ' 20000 ~ 2 * (10 ^ 4)

Hexadecimal, Binary and Octal conversions

VAL can also be used to convert string arguments that are in the form of Hexadecimal, Binary and Octal numbers. Hexadecimal values should be prefixed with "&H" and Binary with "&B". Octal values may be prefixed "&O", "&Q" or just "&". If string_expression contains a leading zero, the result is returned as an unsigned value; otherwise, a signed value is returned. For example:

i& = VAL("&HF5F3") ' Hex, returns -2573 (signed)

j& = VAL("&H0F5F3") ' Hex, returns 62963 (unsigned)

x& = VAL("&B0100101101") ' Binary, returns 301 (unsigned)

y& = VAL("&O4574514") ' Octal, returns 1243468 (signed)

Valid hex characters include 0 to 9, A to F (and a to f). Valid Octal characters include 0 to 7, and binary 0 to 1.

Use the STR$ and FORMAT$ functions to convert numeric values into decimal strings. Use BIN$, HEX$ and OCT$ to convert numeric values into string representations of Binary, Hexadecimal and Octal number system values.

Restrictions

VAL stops analyzing string_expression when non-numeric characters are encountered. When dealing with Hexadecimal, Binary, and Octal number systems, the period character is classified as non-numeric. This is because PowerBASIC only supports floating-point formats for the decimal number system. VAL accepts the period character as a decimal place for all decimal number system values.

VAL does not analyze trailing type-specifiers for decimal strings. For example, VAL("9.1&") is evaluated as 9.1 rather than 9 because the "&" suffix is treated as a non-numeric character, not a type-specifier. However, type suffixes may be used with binary, octal, and hex values.

See also

BIN$, FORMAT$, HEX$, OCT$, STR$

Example

Price$ = "$ 15,345.92"

Cost@@ = VAL(REMOVE$(Price$, ANY "$, "))

Result

15345.92


VARIANT# function

Purpose

Return the numeric value contained in a Variant variable.

Syntax

numericvar = VARIANT#(vrntvar)

Remarks

The value returned by VARIANT# may be any range from BYTE to DOUBLE/QUAD/CURRENCY, depending upon the internal representation used within the Variant.

While Variant variables, by definition, do not offer support for Extended Precision Float data types, you should note that it is possible for a QUAD or CURRENCY value to exceed the precision level offered by a DOUBLE. You should therefore use some judgement in deciding on the variable type to be used as the destination of this function, based upon the expected return values, and the internal representation, which you can obtain with VARIANTVT.

Restrictions

VARIANT# presumes that a valid numeric value is present (not an array); otherwise, the value zero is returned.

See also

DIM, LET, OBJECT, SET, VARIANT$, VARIANTVT

Example

DIM vVnt AS VARIANT

vVnt = 999&

a& = VARIANT#(vVnt)


VARIANT$ function

Purpose

Return the dynamic string value contained in a Variant variable.

Syntax

stringvar = VARIANT$(vrntvar)

Remarks

VARIANT$ automatically converts the Variant from Wide/Unicode format to ANSI.

Restrictions

VARIANT$ presumes that a valid string value is present (not an array); otherwise, an empty string is returned.

See also

DIM, LET, OBJECT, SET, VARIANT#, VARIANTVT

Example

DIM vVnt AS VARIANT

vVnt = "Hello World"

a$ = VARIANT$(vVnt)


VARIANTVT function

Purpose

Determine the internal data type of the data stored in a Variant variable.

Syntax

numericvar = VARIANTVT(vrntvar)

Remarks

The VARIANTVT function returns the internal VT data type stored in the Variant. The entire range of %VT_ prefixed values are documented by the OLE (COM) specification and are available in WIN32API.INC.

The most important values in this limited context include %VT_EMPTY (=0) and %VT_BSTR (=8), since most of the others are formats automatically resolved by the LET statement and VARIANT# function.

Result

Equate

Content Type

0

%VT_EMPTY

0 or Empty string

1

%VT_NULL

Null string

2

%VT_I2

Integer

3

%VT_I4

Long-Integer

4

%VT_R4

Single

5

%VT_R8

Double

6

%VT_CY

Currency

7

%VT_DATE

Date

8

%VT_BSTR

Dynamic String

9

%VT_DISPATCH

Interface reference

10

%VT_ERROR

Error Code

11

%VT_BOOL

Boolean

12

%VT_VARIANT

Variant

13

%VT_UNKNOWN

16

%VT_I1

Byte

17

%VT_UI1

Byte

18

%VT_UI2

Word

19

%VT_UI4

DWORD

20

%VT_I8

Quad (signed)

21

%VT_UI8

Quad (unsigned)

22

%VT_INT

Integer

23

%VT_UNIT

Word

24

%VT_VOID

25

%VT_HRESULT

COM result code

26

%VT_PTR

Pointer

27

%VT_SAFEARRAY

VB Array

28

%VT_CARRAY

29

%VT_USERDEFINED

30

%VT_LPSTR

ANSI string

31

%VT_LPWSTR

Unicode string

64

%VT_FILETIME

65

%VT_BLOB

66

%VT_STREAM

67

%VT_STORAGE

68

%VT_STREAMED_OBJECT

69

%VT_STORED_OBJECT

70

%VT_BLOB_OBJECT

71

%VT_CF

72

%VT_CLSID

Class ID

&H1000

%VT_VECTOR

&H2000

%VT_ARRAY

Array

&H4000

%VT_BYREF

If a Variant contains a complete array, the Variant type is determined by adding the base type to the array modifier. That is, for a array, it would be %VT_BSTR plus %VT_ARRAY (= &H2008).

Quad arrays within Variants are not supported by most versions of Windows. The result from VARIANTVT can be used to see whether such an array was created properly.

See also

DIM, LET, OBJECT, SET, VARIANT#, VARIANT$


VARPTR function

Purpose

Return the 32-bit address of a variable.

Syntax

y = VARPTR(variable)

Remarks

VARPTR returns a complete 32-bit address to the specified variable as a Double-word (DWORD) value. variable is any , , structure variable (User-Defined Type or Union), or element of an array. VARPTR returns a pointer (32-bit address in memory) where the variable data is stored.

VARPTR may also be used to locate an array descriptor, as well as the array data itself. To find the address of an array descriptor, use the array name with empty parentheses: VARPTR( x( ) ).

When you use VARPTR to get the address of a dynamic (variable length) string, keep in mind that the value being returned is the address of the string handle, not the actual data in the string. This can be useful for manipulating a dynamic string array using indexed-pointers, For example:

DIM A$(100), b$, pA AS STRING PTR, x&

' Assume A$() is filled here

pA = VARPTR(a$(0)) ' 1st element handle

FOR X& = 0 TO 100

B$ = B$ + @pA[x&] + ","

NEXT x&

You can use STRPTR to find the address of the string's data. When used with pointers, VARPTR returns the address of the pointer itself.

Restrictions

VARPTR cannot be used on Register variables, because Register variables are stored in internal processor registers rather than application memory. VARPTR can be used on UDT and Union variables, but not the UDT definition name. For example:

TYPE MyType

ABC AS LONG

END TYPE

...

DIM x AS MyTYPE, y&

y& = VARPTR(x) ' This is legal

y& = VARPTR(MyType) ' This is not

See also

PEEK, Pointers, POKE, STRPTR

Example

DIM x AS INTEGER PTR, a%, b%

a% = 55

x = VARPTR(A%)

b% = @x

CALL DisplayResult("b% contains " + FORMAT$(b%))

Result

b% contains 55


VERIFY function

Purpose

Determine whether each character of a is present in another string.

Syntax

x = VERIFY([start&,] MainString, MatchString)

Remarks

VERIFY returns zero if each character in MainString is present in MatchString. If not, it returns the position of the first non-matching character in MainString.

This function is very useful for determining if a string contains only digits, for example.

VERIFY is case-sensitive, so capitalization matters.

Restrictions

If start& evaluates to a position outside of the string on either side, or if start& is zero, VERIFY returns zero.

See also

INSTR, LCASE$, LTRIM$, MID$, REMOVE$, REPLACE, RIGHT$, RTRIM$, TALLY, TRIM$, UCASE$

Example

' returns 4 since "." is not in "0123456789"

x& = VERIFY("123.65,22.5", "0123456789")

' returns 7 since 5 starts it past the first non-digit ("." at position 4)

x& = VERIFY(5,"123.65,22.5", "0123456789")


WAITKEY$ function

Purpose

Read a character or mouse event from the console input buffer, without echoing the character to the screen. If no event is available, WAITKEY$ will wait for an event to arrive in the console input buffer.

Syntax

As a function:

i$ = WAITKEY$

As a statement:

WAITKEY$

Remarks

While waiting, time-slices for the current thread are released back to the operating system, in order to reduce CPU load.

WAITKEY$ returns a of one or two characters that reflects the status of the keyboard buffer. It can also return a string of four bytes if mouse event trapping is enabled. WAITKEY$ will always read keys from the keyboard, regardless of STDIN redirection status.

A string length of one (LEN(i$) = 1) means that an ASCII key was pressed, and the string contains the ASCII character (values between 1 and 31 indicate a control code).

A string length of two (LEN(i$) = 2) means that an extended key was pressed. In this case, the first character in the string has a value of zero, and the second is the extended keyboard code. For example, pressing the F1 key will return CHR$(0, 59).

A string length of four (LEN(i$) = 4) means that a mouse event was retrieved. In this case, the first two characters in the string have a value of 255. The third character is an event code:

Value | Definition
1 | movement
2 | double-click
4 | button press
8 | button release

The fourth character defines the button that was pressed:

Value | Definition
0 | no button
1 | left
2 | right
4 | second left button
8 | third left button
16 | fourth left button

For example, moving the mouse with the left button pressed will return CHR$(255, 255, 1, 1). Button presses can be combined, for example pressing the left and right mouse buttons together will return CHR$(255, 255, 4, 3). The Mouse must be enabled with the MOUSE statement in order to receive mouse events.

WAITKEY$ used as a statement will simply remove a key from the keyboard event. If no key is present, it will wait for the next keyboard event.

See also

ASC, INPUT FLUSH, INKEY$, INSHIFT, INSTAT, LINE INPUT, MOUSE, MOUSESTAT, MOUSEX, MOUSEY, WAITSTAT

Example

FUNCTION PBMAIN() AS LONG

PRINT "Display all CHR$ codes for keyboard and mouse events"

PRINT "Press 'q' to Quit"

PRINT

PRINT "CHR WAITKEY$ ="

' Make the caret visible. Turn mouse events ON,

' and set to trap all mouse events

CURSOR ON

MOUSE ON

MOUSE 3, DOUBLE, MOVE, DOWN, UP

DO

i$ = WAITKEY$ ' Wait for a key or mouse event

IF LEN(i$) = 1 THEN

PRINT " "; i$; " CHR$("; ASC(i$); ")"

IF i$ = "q" THEN EXIT DO

ELSEIF LEN(i$) = 2 THEN

PRINT " CHR$(0,"; ASC(i$, 2); ")"

ELSEIF LEN(i$) = 4 THEN

PRINT "Mouse CHR$(255,255,"; _

ASC(i$, 3); ","; ASC(i$, 4); ")"

END IF

LOOP

END FUNCTION


WAITSTAT statement

Purpose | Wait until a key is pressed or a mouse event occurs. WAITSTAT does not remove the event from the console input buffer.
Syntax | WAITSTAT
Remarks | WAITSTAT literally waits for an event to occur before execution continues. While waiting, time slices are released back to the operating system in order to reduce CPU load.
See also | INKEY$, INSHIFT, INSTAT, MOUSE, WAITKEY$
Example | WAITSTAT ' Wait until an event occurs, then... A$ = INKEY$ ' retrieve the key/mouse event


WHILE/WEND statements

Purpose

Define a block of program statements that are executed repeatedly for as long as certain conditions are met.

Syntax

WHILE integer_expression

{statements}

[EXIT LOOP]

{statements}

WEND

Remarks

If integer_expression is TRUE (it evaluates to a non-zero value), all of the statements between the WHILE and the terminating WEND are executed. PowerBASIC then jumps back to the WHILE statement and repeats the test. If it is still TRUE, PowerBASIC executes the enclosed statements again. This process is repeated until the test expression evaluates to zero, or an EXIT statement is encountered. In either case, execution passes to the statement following WEND.

If integer_expression evaluates to FALSE (zero) on the first pass, none of the statements in the loop are executed.

Loops built with WHILE/WEND statements can be nested (enclosed within each other). Each WEND matches the most recent unmatched WHILE.

One use of a WHILE/WEND loop is to input data from a file until the end of the file is reached:

i& = 0

WHILE ISFALSE EOF(1)

INCR i&

LINE INPUT #1, FileTxt$(i&)

WEND

Although the compiler does not care, it's a good idea to indent the statements between WHILE and WEND, to clarify the structure of the loop you have constructed.

Note that the following code creates in infinite loop:

WHILE -1

...

WEND

To exit a WHILE/WEND loop prematurely, use the EXIT LOOP statement.

PowerBASIC's DO/LOOP construct offers a more flexible way to build conditional loops.

Also see the discussion on the IF statement for notes on PowerBASIC's Short-circuit evaluation and its possible side effects.

See also

DO/LOOP, EXIT, FOR/NEXT, ITERATE, Short-circuit evaluation


WINMAIN function

Purpose

WINMAIN (or its synonym MAIN) is a user-defined function called by Windows to begin execution of an application.

Syntax

FUNCTION {WINMAIN | MAIN} ( _

BYVAL hInstance AS DWORD, _

BYVAL hPrevInst AS DWORD, _

BYVAL lpszCmdLine AS ASCIIZ PTR, _

BYVAL nCmdShow AS LONG ) AS LONG

Remarks

The WINMAIN function is called by Windows when an executable application first loads and begins to run. It is often referred to as the "entry point" for the application. When the execution of WINMAIN is completed, the application is deemed to be finished, and Windows releases the application memory back to the heap. WINMAIN receives the following parameters:

hInstance

The executable's (EXE) instance handle. Each instance of a Windows application has a unique handle. It is used as a parameter to a number of Windows API functions which may need to distinguish between multiple instances of an application.

hPrevInst

Not used by 32-bit Windows. It is present merely for compatibility with existing 16-bit code, and always returns zero in 32-bit applications.

lpszCmdLine

A pointer to an ASCIIZ string that contains a command-line. Note that the string passed in lpszCmdLine is not the same as the string returned by the GetCommandLine API call. The string in lpszCmdLine contains the command-line arguments only (like COMMAND$), but GetCommandLine returns the program name (including path) followed by the arguments.

nCmdShow

Specifies how to display the application's main window. For example, the calling application can specify %SW_NORMAL or %SW_MINIMIZE, etc. It is up to the programmer to honor this parameter, and to do so is recommended.

Return

The return value assigned to WINMAIN is optional, but by convention, the return value is derived from the wParam& parameter of a %WM_QUIT message.

Typically, a GUI-based application uses the WINMAIN function to create the initial GUI application window, and then enters a message loop. This loop should terminate when a %WM_QUIT message is received, and the wParam& parameter of that message should be passed on as the return value for WINMAIN. If WINMAIN terminates before entering the message loop, WINMAIN should return zero.

Console applications may use the return value to set an error level that can be passed back to the calling application, in the range 0 to 255 inclusive. Batch files may act on the result through the IF [NOT] ERRORLEVEL batch command.

If the parameters passed to WINMAIN are not required by the application itself, the PBMAIN function may be used in place of WINMAIN.

Restrictions

Pointers may not be passed BYREF, so the lpszCmdLine parameter of WINMAIN must be declared to be passed BYVAL.

See also

PBMAIN

Example

#COMPILE EXE

FUNCTION WINMAIN(BYVAL hInst???, BYVAL hPrevInst???, BYVAL pCmdLine AS ASCIIZ PTR, BYVAL nCmdShow&) AS LONG

...

FUNCTION = 1

END FUNCTION


WRITE# statement

Purpose

Output data to a sequential file in a delimited format.

Syntax

WRITE #filenum&, [expression [{,|;} expression] ...]

Remarks

WRITE# is similar to PRINT#, except WRITE# inserts a comma in the output file between each expression. It encloses data within quotation marks, and adds no leading or trailing spaces around values.

WRITE# is the preferred method of writing data to a sequential file, since it formats the output to be readable by the INPUT# statement. In other words, INPUT# respects the delimiter characters that separate items in a line of text, as created by WRITE#.

filenum&

The file number used when the file or device was opened.

expression

A numeric or a string expression representing the data to be written to the file or device. WRITE# with a file number and a comma but no expressions, outputs a carriage return to the file. To read a delimited file without regard to the delimiter characters, use the LINE INPUT# statement.

Restrictions

For best results, strings should not contain quotation marks, as these may interfere with the expected output format.

Each expression in the WRITE# statement must be separated from other expressions by a comma or semicolon. Unlike the PRINT# statement, WRITE# does not allow a trailing comma or semicolon.

See also

GET, GET$, INPUT#, LINE INPUT#, OPEN, PRINT#, PUT, PUT$, SETEOF

Example

' Open a sequential output file and write to it

OPEN "FILE.TXT" FOR OUTPUT AS #1

WRITE #1, "TEST"

z& = -12345&

info1$ = "Do not covet"

info2$ = "thy neighbors ox"

WRITE #1, z&, info1$, info2$

WRITE #1, "TEST"

CLOSE #1

Result

"TEST"

-12345,"Do not covet","thy neighbors ox"

"TEST"


XOR operator

Purpose

The XOR operator works as both a logical and a bitwise arithmetic operator.

Syntax

p XOR q

Remarks

XOR as a logical operator

XOR returns FALSE (zero) if and only if both its operands have the same value. Here is XOR's truth table:

Truth table
x | y | x XOR y
T | T | F
T | F | T
F | T | T
F | F | F

Using XOR as a bitwise arithmetic operator

An XOR mask complements (reverses) selected bits of an value, without affecting the other bits of that value. For example, to complement the two most-significant bits in &H9700, use XOR with a mask of &HC000; that is, all zeros except for the positions to be complemented:

See also

Arithmetic Operators, AND, EQV, IMP, NOT, OR


XPRINT$ function

Purpose

Return the name of the attached host printer.

Syntax

device$ = XPRINT$

Remarks

XPRINT$ returns the name of the attached host printer, which is the printer that would be used by statements. If there is no attached host printer, an empty is returned. XPRINT$ is typically used to detect if an XPRINT ATTACH operation was successful.

See also

XPRINT ATTACH, XPRINT CLOSE


XPRINT statement

Purpose

Output text to a host-printer device.

Syntax

XPRINT [expression] [;]

Remarks

The XPRINT functionality is similar to the PRINT statement, except that the data is sent to a host printer rather than to a display. A host printer is a Windows-Only printer which uses a device driver to form text and graphics.

PowerBASIC inserts a carriage return and linefeed at the end of each printed line. A semi-colon between expressions is an optional delimiter which leaves the printer column position unchanged. A trailing semi-colon suppresses the final CR/LF.

Before you execute an XPRINT statement, you must explicitly connect to the intended host printer using the XPRINT ATTACH statement. If the connection to the device is unsuccessful, all XPRINT statements are ignored until a valid printer device has been attached.

Once all the data has been sent to the printer, detach the printer so other applications can use it., with the XPRINT CLOSE statement.

Host-based (Windows-only) printers use proprietary control protocols. Generally speaking, you should avoid the embedding of control codes like $CR, $LF, and $FF in a string expression, as they may not produce the expected results. Instead, use XPRINT without a parameter to move to the next line, or XPRINT FORMFEED to start a new page. If the XPRINT operation is not successful, an appropriate error is generated.

See also

LPRINT, XPRINT ATTACH, XPRINT CHR SIZE, XPRINT COLOR, XPRINT CLOSE, XPRINT FONT, XPRINT GET POS, XPRINT SET POS, XPRINT TEXT SIZE

Example

' Typical XPRINT printing strategy

ERRCLEAR

XPRINT ATTACH DEFAULT ' Use default printer

IF ERR = 0 AND LEN(XPRINT$) > 0 THEN

XPRINT "This is your printer talking"

XPRINT FORMFEED ' Issue a formfeed

XPRINT CLOSE ' detach the printer

END IF


XPRINT ARC statement

Purpose

Draw an arc on a host printer page.

Syntax

XPRINT ARC (x1!, y1!) - (x2!, y2!), arcStart!, arcEnd! [, rgbColor&]

Remarks

An arc is a section of a circle or an ellipse. To specify a particular arc, you would first define the full circle or ellipse of which it is a part, and then specify the points on the ellipse where the arc starts and stops.

The full circle or ellipse is defined by its bounding rectangle, which is the smallest rectangle which can be drawn around the circle or ellipse. For example, if the circle is centered at position (400,400), with a radius of 100 pixels, the upper left corner (x1,y1) of the bounding rectangle is (300,300), and the lower right corner (x2,y2) is (500,500).

The start point and end point of the arc are specified by their angle, which must be given in radians. A complete circle or ellipse is 2*pi radians. On a 12-hour clock-face, the values 0 and 2*pi both refer to the position of 3 o'clock, while the value 1*pi refers to the position of 9 o'clock. Other positions are specified by a radian value relative to these. In PowerBASIC, arcs are always drawn counter-clockwise from the starting point to the ending point.

Prior to any operations, a host printer must first be selected with XPRINT ATTACH. The coordinate points are specified in pixels (or world coordinates, if those were chosen with XPRINT SCALE). Line width can be set using XPRINT WIDTH. If line width is set to 1 (the default), the line style can be set with XPRINT STYLE. Because of the nature of an arc, XPRINT ARC neither uses, nor updates, (last point referenced). If executed without a host printer attached, error 57 is generated.

x1!, y1!

The upper left corner of the bounding rectangle of the full circle or ellipse.

x2!, y2!

The lower right corner of the bounding rectangle of the full circle or ellipse.

ArcStart!

The starting angle of the arc, in radians, from 0 to 2*pi.

ArcEnd!

The ending angle of the arc, in radians, from 0 to 2*pi radians. Note that arcs are always drawn counter-clockwise from arcStart! to arcEnd!. Compared with a 12-hour clock-face, 0 or 2*pi radians is at 3 o'clock, and 1*pi radians is at 9 o'clock.

rgbColor&

Optional RGB color for the arc. If omitted (or -1), the current foreground color for the host printer page is used.

See also

XPRINT ATTACH, XPRINT COLOR, XPRINT ELLIPSE, XPRINT PIE, XPRINT STYLE, XPRINT WIDTH

Example

' Draw two arcs that combine into a circle.

' The upper half uses the default foreground color.

' The lower half is drawn in red.

LOCAL Pi AS DOUBLE

Pi = 4 * ATN(1) ' Calculate Pi

XPRINT ARC (5, 5) - (105, 105), 0, Pi ' Upper half

XPRINT ARC (5, 5) - (105, 105), Pi, 0, %RED ' Lower half


XPRINT ATTACH statement

Purpose

Connect a host-based (GDI) printer for use with .

Syntax

XPRINT ATTACH {CHOOSE | DEFAULT | PrinterName$} [,JobName$]

Remarks

XPRINT ATTACH connects to a host-based (Windows-only or GDI-based) printer for use with subsequent XPRINT operations. Host-based printing is device-independent and performed through the Windows printing system and printer driver. Device independence can be achieved because the printer driver handles the task of converting text into the manufacturers proprietary binary format used by the printer.

To send device-dependent print data (such as plain text) to a line printer device, use the LPRINT ATTACH statement instead.

XPRINT ATTACH allows you to change the printer device used by XPRINT operation. When executed, the current connection (if any) is closed and the new connection is established.

DEFAULT

If DEFAULT is specified, the default printer (as set in the Printers applet in Control Panel) is used. For example:

XPRINT ATTACH DEFAULT

CHOOSE

If CHOOSE is specified, the Choose Printer common dialog is opened, allowing the user to select from the list of installed printers. For example:

XPRINT ATTACH CHOOSE

PrinterName$

The name of the printer to attach (as shown in the Printers applet in Control Panel, or returned by the PRINTER$ function). printername$ must be a valid device name and cannot exceed 259 characters in length. For example:

XPRINT ATTACH "HP LaserJet 5MP"

JobName$

The name of the print job. This will be shown in the print spooler. If you do not supply a name, "Printjob" is used by default.

If XPRINT ATTACH is not successful, XPRINT$ returns an empty . Error 68 ("device unavailable") is generated if an invalid printer was specified. No error is generated if the user cancels the Choose Printer dialog (with XPRINT ATTACH CHOOSE). Therefore, for host-based printing, applications should always use XPRINT ATTACH to explicitly select the intended host-based printer, then test for a successful selection with the XPRINT$ and ERR functions to ensure the host-based printer selection was successful.

Unlike direct printing (LPRINT ATTACH), host-based printing is handled by a printer driver and the operating system's spooler subsystem. Therefore, spooler settings such as "work offline" in the Printer Properties dialog will not impede the creation of a spooled print job. Once all the data has been sent to the printer, detach the printer so other applications can use it., with the XPRINT CLOSE statement.

Host-based printers use proprietary control protocols, unlike line printers, so it is usually not possible to send them printer-dependent control codes. To attach a line printer, use LPRINT ATTACH instead of XPRINT ATTACH.

Note: You can enumerate the available printers with the PRINTERCOUNT and PRINTER$ functions.

See also

LPRINT ATTACH, PRINTER$, XPRINT CANCEL, XPRINT CLOSE, XPRINT$

Example

ERRCLEAR

XPRINT ATTACH "HP DeskJet 960c"

IF ERR = 0 AND LEN(XPRINT$) > 0 THEN

XPRINT COLOR RGB(0,0,255) ' Blue

XPRINT "This is your printer talking"

XPRINT FORMFEED ' Issue a formfeed

XPRINT CLOSE ' Deselect the printer

END IF


XPRINT BOX statement

Purpose

Draw a box with square or rounded corners on a host printer page.

Syntax

XPRINT BOX (x1!, y1!) - (x2!, y2!) [, [corner&] [, [rgbColor&] [, [fillcolor&] [, [fillstyle&]]]]]

Remarks

Prior to any operations, a host printer must first be selected with XPRINT ATTACH. The coordinate points are specified in pixels (or world coordinates, if those were chosen with XPRINT SCALE). Line width can be set using XPRINT WIDTH. If line width is set to 1 (the default), the line style can be set with XPRINT STYLE. Because of the nature of a box, XPRINT BOX neither uses, nor updates, (last point referenced). If executed without a host printer attached, error 57 is generated.

x1!, y1!

The upper left corner of the box.

x2!, y2!

The lower right corner of the box.

corner&

The percentage of roundness of the corners, in the range of 0 to 100. A value of zero creates square corners, while 100 creates a circle/oval. A value of 20 being most common for a pleasant, rounded appearance. If corner& is omitted, the default is 0, which creates a rectangle with square corners.

rgbColor&

Optional RGB color of the box edge. If omitted (or -1), the edge color defaults to the current foreground color for the host printer page.

fillcolor&

Optional RGB color of the box interior. If fillcolor& is omitted (or -2), the interior of the box is not filled, allowing the background to show through. If fillcolor& is -1, the interior is painted with the same color as the edge. Otherwise, fillcolor& specifies the RGB color to be used.

fillstyle&

Optional fill style (pattern) to be used. If fillstyle& is omitted, the default fill style is solid (0). If a hatch pattern is chosen (1 to 6), the foreground color is specified by the fillcolor&, while the background is specified by the default background color for the host printer page. The optional fillstyle& may be:

0 | Solid (default)
1 | Horizontal Lines
2 | Vertical Lines
3 | Upward Diagonal Lines
4 | Downward Diagonal Lines
5 | Crossed Lines
6 | Diagonal Crossed Lines

See also

XPRINT ATTACH, XPRINT COLOR, XPRINT LINE, XPRINT STYLE, XPRINT WIDTH

Example

' Draw rectangle with square corners and default colors.

XPRINT BOX (10, 10) - (100, 80)

' Draw a blue rectangle with 20% rounded corners,

' filled with a light-gray, diagonal cross pattern

XPRINT BOX (15, 15) - (95, 75), 20, %BLUE, RGB(191,191,191), 6


XPRINT CANCEL statement

Purpose | Cancel a print job on the host printer.
Syntax | XPRINT CANCEL
Remarks | XPRINT CANCEL deletes the current print job and detaches the host printer, as long as XPRINT CLOSE has not yet been executed. This function is generally used to abort the print process when an error occurs.
See also | XPRINT ATTACH, XPRINT CLOSE


XPRINT CHR SIZE statement

Purpose

Retrieve the character size for the current font on a host printer page.

Syntax

XPRINT CHR SIZE TO ncWidth!, ncHeight!

Remarks

The character size is specified in pixels (or world coordinates, if they have been defined with an XPRINT SCALE statement).

If the font is a fixed-width font, like Courier New or Lucida Console, the sizes returned are as exact as possible, given the rounding errors possible when converting from pixels to other coordinates. If the font is proportional, like Arial or Times New Roman, the width will be the average for the entire font.

If a host printer is not attached, error 57 is generated.

See also

XPRINT, XPRINT ATTACH, XPRINT FONT, XPRINT TEXT SIZE


XPRINT CLOSE statement

Purpose | Detach a host printer so printing may begin.
Syntax | XPRINT CLOSE
Remarks | XPRINT CLOSE detaches the printer from the current process, and allows printing to a HOST printer to begin. If XPRINT CLOSE is not executed, printed data may be lost.
See also | XPRINT ATTACH, XPRINT CANCEL


XPRINT COLOR statement

Purpose

Set the foreground color (and, optionally, the background color) for various statements.

Syntax

XPRINT COLOR foreground& [, background&]

Remarks

Colors are expressed as RGB values, or use -1 for the default color. If the background parameter is -2, the background is not painted, allowing the content behind to become visible. Otherwise, the specified RGB color is used.

A host printer must first be connected with XPRINT ATTACH. If a host printer is not attached, error 57 is generated.

See also

XPRINT, XPRINT ATTACH

Example

' Set colors to red foreground and blue background.

XPRINT COLOR %RED, RGB(0,0,191)


XPRINT COPY statement

Purpose

Copy a to a host printer page.

Syntax

XPRINT COPY hbmpSource???, id& [, style&]

XPRINT COPY hbmpSource???, id& TO (x!, y!) [, style&]

XPRINT COPY hbmpSource???, id&, (x1!, y1!)-(x2!, y2!) TO (x!, y!) [, style%]

Remarks

You can copy a complete bitmap, or a portion of it, to the host printer page. The expression hbmpSource??? specifies the handle of the source bitmap or window. The expression id& is reserved for future use, and must always be set to zero. The destination of the copy operation is the host printer page. You must use care that your parameters are valid for the specified bitmaps, or results of the operation are undefined.

The first form of the XPRINT COPY statement copies the complete bitmap, positioning it at (0,0), which is the upper left corner of the destination.

The second form of XPRINT COPY also copies the complete bitmap, but positions it at the point specified by the parameter (x!, y!).

The third form copies a portion of the bitmap, positioning it at the point specified by the parameter (x!, y!). If style& is included, it is one of the following values:

%mix_NotMergeSrc | Pixel is the inverse of the MergeSrc color.
%mix_NotCopySrc | Pixel is the inverse of the pen color.
%mix_MaskSrcNot | Pixel is a combination of the colors common to both the source and the inverse of the pixel.
%mix_XorSrc | Pixel is a combination of the colors in the source and in the pixel, but not in both.
%mix_MaskSrc | Pixel is a combination of the colors common to both the source and the pixel.
%mix_MergeNotSrc | Pixel is a combination of the source color and the inverse of the pixel color.
%mix_CopySrc | Pixel is the source color (default).
%mix_MergeSrc | Pixel is a combination of the source color and the pixel color.

A host printer must first be connected with XPRINT ATTACH. If a host printer is not attached, error 57 is generated.

See also

XPRINT ATTACH, XPRINT RENDER, XPRINT STRETCH


XPRINT ELLIPSE statement

Purpose

Draw an ellipse or a circle on a host printer page.

Syntax

XPRINT ELLIPSE (x1!, y1!) - (x2!, y2!) [, [rgbColor&] [, [fillcolor&] [, [fillstyle&]]]]

Remarks

A host printer must first be connected with XPRINT ATTACH. The coordinate points are specified in pixels (or world coordinates, if those were defined with an XPRINT SCALE statement). Line width can be set using XPRINT WIDTH. If line width is set to 1 (the default), the line style can be set with XPRINT STYLE. Because of the nature of an ellipse, which has no obvious beginning or end, XPRINT ELLIPSE neither uses, nor updates, the last point referenced (POS). If executed without a host printer attached, error 57 is generated.

x1!, y1!

The upper left corner of the bounding rectangle.

x2!, y2!

The lower right corner of the bounding rectangle.

rgbColor&

Optional RGB color of the ellipse edge. If omitted (or -1), the edge color defaults to the current foreground color for the host printer page.

fillcolor&

Optional RGB color of the ellipse interior. If fillcolor& is omitted (or -2), the interior of the ellipse is not filled, allowing the background to show through. If fillcolor& is -1, the interior is painted with the same color as the edge. Otherwise, fillcolor& specifies the RGB color to be used.

fillstyle&

Optional fill style (pattern) to be used. If fillstyle& is omitted, the default fill style is solid (0). If a hatch pattern is chosen (1 to 6), the foreground color is specified by the fillcolor&, while the background is specified by the default background color for the host printer page. The optional fillstyle& may be:

0 | Solid (default)
1 | Horizontal Lines
2 | Vertical Lines
3 | Upward Diagonal Lines
4 | Downward Diagonal Lines
5 | Crossed Lines
6 | Diagonal Crossed Lines

See also

XPRINT ARC, XPRINT ATTACH, XPRINT COLOR, XPRINT LINE, XPRINT PIE, XPRINT STYLE, XPRINT WIDTH

Example

' Draw a circle, using default colors.

XPRINT ELLIPSE (10, 10) - (100, 100)

' Draw a blue ellipse filled with a light-gray,

' diagonal cross pattern.

XPRINT ELLIPSE (15, 25) - (95, 50), %BLUE, RGB(191,191,191), 6


XPRINT FONT statement

Purpose | Select a font to be used by the XPRINT statement.
Syntax | XPRINT FONT fontname$, points&, style&
Remarks | A host printer must first be connected with XPRINT ATTACH. If no XPRINT FONT statement is executed, the default font is Courier New with no style attributes. If XPRINT FONT is unsuccessful, an error is generated. If the requested font is not available on the computer, Windows will normally substitute another font, which may have similar attributes.
fontname$ | Name of the font.
points& | Size of the font, in points.
style& | Font style attribute. Any of the following values can be combined or used alone: 0 = normal 1 = bold 2 = italic 4 = underline 8 = strikeout For example, a style& value of 3 specifies a combination of both bold and italic attributes.
See also | XPRINT, XPRINT ATTACH, XPRINT CHR SIZE, XPRINT TEXT SIZE
Example | ' Set the font for the host printer ' to Times New Roman, 18 points, ‘ bold + italic + underline (1+2+4). XPRINT FONT "Times New Roman", 18, 7


XPRINT FORMFEED statement

Purpose | Start a new page for the host printer.
Syntax | XPRINT FORMFEED
Remarks | XPRINT FORMFEED causes the current print page to be ejected, and a new page started. If XPRINT FORMFEED is unsuccessful, an error is generated. Note that some printers do not eject a page if it is blank.
See also | XPRINT ATTACH, XPRINT CLOSE


XPRINT GET CLIENT statement

Purpose | Retrieve the size of the client area (printable area) on the host printer page.
Syntax | XPRINT GET CLIENT TO ncWidth!, ncHeight!
Remarks | Sizes are specified in pixels (or world coordinates, if those were defined with an XPRINT SCALE statement). If executed without a host printer attached, error 57 is generated.
See also | XPRINT ATTACH, XPRINT GET MARGIN, XPRINT GET PPI, XPRINT GET SIZE


XPRINT GET DC statement

Purpose | Retrieve the handle of the device context (DC) for the host printer page.
Syntax | XPRINT GET DC TO hDC???
Remarks | If no host printer is currently attached, zero is returned. The DC handle may be used with various Windows API functions to perform specialized operations on the host printer page.
See also | XPRINT ATTACH


XPRINT GET LINES statement

Purpose

Retrieve the number of lines that can be printed.

Syntax

XPRINT GET LINES TO linecount&

Remarks

XPRINT GET LINES retrieves the number of lines of text which can be printed on the host printer page, given the current selected font. Since statements do not generate an automatic formfeed when text is printed on the last line, this statement can be used to determine when your program should execute an XPRINT FORMFEED to move to the next printed page on a host printer. If executed without a host printer attached, error 57 is generated.

See also

XPRINT, XPRINT ATTACH, XPRINT FONT, XPRINT FORMFEED


XPRINT GET MARGIN statement

Purpose | Retrieve the margin sizes for the host printer.
Syntax | XPRINT GET MARGIN TO nLeft!, nTop!, nRight!, nBottom!
Remarks | XPRINT GET MARGIN retrieves the size of the margins (the non-printable area) of the printer page. This is important because some printers do not provide equal margins on each side of the page. This is more common on the vertical coordinate, but could be found in either or both directions. The size of the four margins are specified in pixels (or world coordinates, if those were defined with an XPRINT SCALE statement). If executed without a host printer attached, error 57 is generated.
See also | XPRINT ATTACH, XPRINT GET CLIENT, XPRINT GET PPI, XPRINT GET SIZE


XPRINT GET MIX statement

Purpose

Retrieve the color mix mode for a host printer page.

Syntax

XPRINT GET MIX TO mode&

Remarks

Prior to any operations, a host printer must first be selected with XPRINT ATTACH. There are 16 mix modes available to use for mixing the drawing color with the color that already exists at the drawing location. The mix mode equates are predefined in PowerBASIC. If executed without a host printer attached, error 57 is generated.

%mix_Blackness | Pixel is always 0 (black).
%mix_NotMergeSrc | Pixel is the inverse of the MergeSrc color.
%mix_MaskNotSrc | Pixel is a combination of the colors common to both the pixel and the inverse of the source.
%mix_NotCopySrc | Pixel is the inverse of the pen color.
%mix_MaskSrcNot | Pixel is a combination of the colors common to both the source and the inverse of the pixel.
%mix_Not | Pixel is the inverse of the pixel color.
%mix_XorSrc | Pixel is a combination of the colors in the source and in the pixel, but not in both.
%mix_NotMaskSrc | Pixel is the inverse of the MaskSrc color.
%mix_MaskSrc | Pixel is a combination of the colors common to both the source and the pixel.
%mix_NotXorSrc | Pixel is the inverse of the XorSrc color.
%mix_Nop | Pixel remains unchanged.
%mix_MergeNotSrc | Pixel is a combination of the source color and the inverse of the pixel color.
%mix_CopySrc | Pixel is the source color (default).
%mix_MergeSrcNot | Pixel is a combination of the source color and the inverse of the pixel color.
%mix_MergeSrc | Pixel is a combination of the source color and the pixel color.
%mix_Whiteness | Pixel is always 1 (white).

See also

XPRINT ATTACH, XPRINT SET MIX


XPRINT GET ORIENTATION statement

Purpose | Retrieve the paper orientation for a host printer page.
Syntax | XPRINT GET ORIENTATION TO orent&
Remarks | XPRINT GET ORIENTATION retrieves the orientation of the paper in the host printer, assigning the value to the long integer variable specified by orent&. The value 1 indicates portrait mode, while 2 indicates landscape mode. If the printer does not support paper orientation, 0 is returned. If a host printer is not attached, error 57 is generated.
See also | XPRINT ATTACH, XPRINT SET ORIENTATION


XPRINT GET PIXEL statement

Purpose | Retrieve the color of a pixel on a host printer page.
Syntax | XPRINT GET PIXEL (x!, y!) TO rgbColor&
Remarks | Not all printer drivers support the ability to retrieve the color of a pixel. If this feature is not supported, or if the coordinates are outside the printer client area, an invalid color value of -1 is returned. If no host printer is attached, error 57 is generated.
See also | XPRINT ATTACH, XPRINT COLOR, XPRINT SET PIXEL


XPRINT GET POS statement

Purpose

Retrieve the last point referenced (POS) by an statement.

Syntax

XPRINT GET POS TO x!, y!

Remarks

XPRINT GET POS allows you to retrieve the last point referenced (POS) by XPRINT statements. The coordinate points are specified in pixels (or world coordinates, if those were defined with an XPRINT SCALE statement). If executed without a host printer attached, an error 57 is generated, and the values 0,0, are returned.

See also

XPRINT ATTACH, XPRINT SET POS


XPRINT GET PPI statement

Purpose | Retrieve the resolution of the host printer page.
Syntax | XPRINT GET PPI TO x&, y&
Remarks | XPRINT GET PPI retrieves the resolution (points per inch) of the host printer page. The resolution is always specified in pixels, regardless of any XPRINT SCALE option. If executed without a host printer attached, error 57 is generated, and the values 0,0 are returned. This statement is particularly useful in drawing items such as rulers and graphs to a particular physical size. There are 25.4 millimeters per inch, so just divide by 25.4 to convert from pixels per inch to pixels per millimeter.
See also | XPRINT ATTACH, XPRINT GET CLIENT, XPRINT GET MARGIN, XPRINT GET SIZE


XPRINT GET QUALITY statement

Purpose | Retrieve the print quality setting for the host printer.
Syntax | XPRINT GET QUALITY TO qual&
Remarks | XPRINT GET QUALITY retrieves the print quality setting for the host printer. The value 1 is draft mode, 2 is low resolution, 3 is medium resolution, and 4 is high resolution. If the printer does not support print quality settings, 0 is returned. If no host printer is attached, error 57 is generated.
See also | XPRINT ATTACH, XPRINT SET QUALITY


XPRINT GET SIZE statement

Purpose | Retrieve the total size of the host printer page.
Syntax | XPRINT GET SIZE TO nWidth!, nHeight!
Remarks | XPRINT GET SIZE allows you to retrieve the full size of the host printer page, including both the printable client area and unprintable margins. The sizes are specified in pixels (or world coordinates, if those were defined with an XPRINT SCALE statement). If no host printer is attached, error 57 is generated, and the values 0,0 are returned.
See also | XPRINT ATTACH, XPRINT GET CLIENT, XPRINT GET MARGIN, XPRINT GET MIX, XPRINT GET PPI


XPRINT LINE statement

Purpose

Draw a line on a host printer page.

Syntax

XPRINT LINE [STEP] [(x1!, y1!)] - [STEP] (x2!, y2!)[, rgbColor&]

Remarks

The line is drawn from the first point, up to, but not including the second point. Coordinate points are specified in pixels, unless optional world coordinates have been defined with an XPRINT SCALE statement. Line width can be set using XPRINT WIDTH. If line width is set to 1 (the default), the line style can be set with XPRINT STYLE. If executed without a host printer attached, error 57 is generated.

x1!, y1!

Optional values which define the starting point of the line. If this optional first point is omitted, the line begins at the last point referenced ( ) in a preceding statement. If the first STEP option is included, the x1! and y1! starting coordinates are relative to the last point referenced (POS) on the host printer page.

x2!, y2!

The ending point of the line. If the second STEP option is included, the x2! and y2! ending coordinates are relative to the starting coordinates.

rgbColor&

Optional RGB color value for the line. If rgbColor& is omitted (or -1), the line color defaults to the current foreground color for the host printer page.

See also

XPRINT ARC, XPRINT ATTACH, XPRINT BOX, XPRINT COLOR, XPRINT ELLIPSE, XPRINT PIE, XPRINT POLYGON, XPRINT POLYLINE, XPRINT SET MIX, XPRINT STYLE, XPRINT WIDTH

Example

' Draw a triangle. Note that, since LINE draws up to,

' but not including the second point, one extra point

' must be added when STEP is used.

XPRINT LINE (10, 10) - (10, 100) ' left side

XPRINT LINE STEP - (101, 100) ' base line

XPRINT LINE STEP - (10, 10) ' back to top


XPRINT PIE statement

Purpose

Draw a pie section on a host printer page.

Syntax

XPRINT PIE (x1!, y1!) - (x2!, y2!), arcStart!, arcEnd! [, [rgbColor&] [, [fillcolor&] [, [fillstyle&]]]]

Remarks

A pie section is an arc, with a line drawn from each end point to the center of the circle or ellipse. To specify a pie section, you would first define the full circle or ellipse of which it is a part, and then specify the points on the ellipse where the arc starts and stops.

The full circle or ellipse is defined by its bounding rectangle, which is defined as the smallest rectangle which can be drawn around the circle or ellipse. For example, if the circle is centered at position (400,400), with a radius of 100 pixels, the upper left corner (x1,y1) of the bounding rectangle is (300,300), and the lower right corner (x2,y2) is (500,500).

The start point and end point of the arc are specified by their angle, which must be given in radians. A complete circle or ellipse is 2*pi radians. On a 12-hour clock-face, the values 0 and 2*pi both refer to the position of 3 o'clock, while the value 1*pi refers to the position of 9 o'clock. Other positions are specified by a radian value relative to these. In PowerBASIC, arcs are always drawn counter-clockwise from the starting point to the ending point.

Prior to any operations, a host printer must first be selected with XPRINT ATTACH. The coordinate points are specified in pixels (or world coordinates, if those were chosen with XPRINT SCALE). Line width can be set using XPRINT WIDTH. If line width is set to 1 (the default), the line style can be set with XPRINT STYLE. Because of the nature of a pie section, XPRINT PIE neither uses, nor updates, (last point referenced). If executed without a host printer attached, error 57 is generated.

x1!, y1!

The upper left corner of the bounding rectangle of the full circle or ellipse.

x2!, y2!

The lower right corner of the bounding rectangle of the full circle or ellipse.

ArcStart!

The starting angle of the arc, in radians, from 0 to 2*pi.

ArcEnd!

The ending angle of the arc, in radians, from 0 to 2*pi radians. Note that arcs are always drawn counter-clockwise from arcStart! to arcEnd!. Compared with a 12-hour clock-face, 0 or 2*pi radians is at 3 o'clock, and 1*pi radians is at 9 o'clock.

rgbColor&

Optional RGB color of the pie edge. If omitted (or -1), the edge color defaults to the current foreground color for the host printer page.

fillcolor&

Optional RGB color of the pie interior. If fillcolor& is omitted (or -2), the interior of the pie is not filled, allowing the background to show through. If fillcolor& is -1, the interior is painted with the same color as the edge. Otherwise, fillcolor& specifies the RGB color to be used.

fillstyle&

Optional fill style (pattern) to be used. If fillstyle& is omitted, the default fill style is solid (0). If a hatch pattern is chosen (1 to 6), the foreground color is specified by the fillcolor&, while the background is specified by the default background color for the host printer page. The optional fillstyle& may be:

0 | Solid (default)
1 | Horizontal Lines
2 | Vertical Lines
3 | Upward Diagonal Lines
4 | Downward Diagonal Lines
5 | Crossed Lines
6 | Diagonal Crossed Lines

See also

XPRINT ARC, XPRINT ATTACH, XPRINT BOX, XPRINT COLOR, XPRINT ELLIPSE, XPRINT LINE, XPRINT STYLE, XPRINT WIDTH

Example

' A full circle is 2*pi radians (100%).

' To show a 25% Pie, use the formula 0.25 * 2 * pi.

' The following divides a full circle into four 25% parts, each

' with its own colors, each slightly separated from the others.

' Note: 0 is at 3 o'clock, then it builds counter-clockwise.

LOCAL Pi2 AS DOUBLE

Pi2 = ATN(1)* 8 ' 2 * Pi can be useful here

XPRINT PIE (10, 9)-(110, 109), 0, Pi2 * 0.25, %BLUE, %LTGRAY, 3

XPRINT PIE (9, 9)-(109, 109), Pi2 * 0.25, Pi2 * 0.50, %RED, %LTGRAY, 4

XPRINT PIE (9, 10)-(109, 110), Pi2 * 0.5, Pi2 * 0.75, RGB(0,127,0), %LTGRAY, 3

XPRINT PIE (10, 10)-(110, 110), Pi2 * 0.75, 0, %GRAY, %LTGRAY, 4


XPRINT POLYGON statement

Purpose

Draw a polygon on a host printer page.

Syntax

XPRINT POLYGON points [, [rgbColor&] [, [fillcolor&] [, [fillstyle&] [, fillmode&]]]]

Remarks

The coordinate points are specified in pixels, unless optional world coordinates have been defined with an XPRINT SCALE statement. Line width can be set using XPRINT WIDTH. If line width is set to 1 (the default), the line style can be set with XPRINT STYLE. XPRINT POLYGON neither uses, nor updates, the last point referenced ( ). If executed without a host printer attached, error 57 is generated.

points

User-defined type that defines the number of vertices and the location of each. There must be at least two, and no more than 1024 vertices. The first member is a long integer point count, followed directly by the appropriate number of single precision floats to specify the actual coordinates. Floating point coordinates are required, because of the possibility of their use as world coordinates with XPRINT SCALE. You can use a type with a scalar list, like this:

TYPE PolyPoints

count as long

x1 as single

y1 as single

x2 as single

y2 as single

x3 as single

y3 as single

END TYPE

Or, you can create an array using point types, like this:

TYPE PolyPoint

x as single

y as single

END TYPE

TYPE PolyArray

count as long

xy(1 TO 3) as PolyPoint

END TYPE

rgbColor&

Optional RGB color of the polygon edge. If omitted (or -1), the edge color defaults to the current foreground color for the host printer page.

fillcolor&

Optional RGB color of the polygon interior. If fillcolor& is omitted (or -2), the interior of the ellipse is not filled, allowing the background to show through. If fillcolor& is -1, the interior is painted with the same color as the edge. Otherwise, fillcolor& specifies the RGB color to be used.

fillstyle&

Optional fill style (pattern) to be used. If fillstyle& is omitted, the default fill style is solid (0). If a hatch pattern is chosen (1 to 6), the foreground color is specified by the fillcolor&, while the background is specified by the default background color for the host printer page. The optional fillstyle& may be:

0 | Solid (default)
1 | Horizontal Lines
2 | Vertical Lines
3 | Upward Diagonal Lines
4 | Downward Diagonal Lines
5 | Crossed Lines
6 | Diagonal Crossed Lines

fillmode&

If fillmode& is missing (or zero), the winding mode is selected. This fills any region with a non-zero winding value. If fillmode& is non-zero, the alternate mode is selected. This fills the area between odd-numbered and even-numbered polygon sides on each scan line. That is, it fills the area between the first side and the second side, between the third side and fourth side, etc.

See also

XPRINT ARC, XPRINT ATTACH, XPRINT BOX, XPRINT COLOR, XPRINT ELLIPSE, XPRINT LINE, XPRINT POLYLINE


XPRINT POLYLINE statement

Purpose

Draw a series of connected lines on a host printer page.

Syntax

XPRINT POLYLINE points [, rgbColor&]

Remarks

The coordinate points are specified in pixels, unless optional world coordinates have been defined with an XPRINT SCALE statement. Line width can be set using XPRINT WIDTH. If line width is set to 1 (the default), the line style can be set with XPRINT STYLE. XPRINT POLYLINE neither uses, nor updates, the last point referenced ( ). If executed without a host printer attached, error 57 is generated.

points

User-defined type that defines the number of vertices and the location of each. There must be at least two, and no more than 1024 vertices. The first member is a long integer point count, followed directly by the appropriate number of single precision floats to specify the actual coordinates. Floating point coordinates are required, because of the possibility of their use as world coordinates with SCALE. You can use a type with a scalar list, like this:

TYPE PolyPoints

count as long

x1 as single

y1 as single

x2 as single

y2 as single

x3 as single

y3 as single

END TYPE

Or, you can create an array using point types, like this:

TYPE PolyPoint

x as single

y as single

END TYPE

TYPE PolyArray

count as long

xy(1 TO 3) as PolyPoint

END TYPE

rgbColor&

Optional RGB color of the polygon edge. If omitted (or -1), the edge color defaults to the current foreground color for the host printer page.

See also

XPRINT ARC, XPRINT ATTACH, XPRINT BOX, XPRINT COLOR, XPRINT ELLIPSE, XPRINT LINE, XPRINT POLYGON, XPRINT STYLE, XPRINT WIDTH


XPRINT RENDER statement

Purpose | Render an image on a host printer page.
Syntax | XPRINT RENDER BmpName$, (x1!, y1!)-(x2!, y2!)
Remarks | Renders an image, loaded from a resource or a disk file, on a host printer page. The parameter BmpName$ contains the name of an image to be loaded. If BmpName$ contains a period, it is presumed to be the name of a disk file. Otherwise, an attempt is made to load it from the program’s resource data; if not found, it is then presumed to be a disk file. The parameters x1!,. y1! define the upper left corner of the destination rectangle, while x2!, y2! define the lower right corner of that rectangle. If the destination rectangle is larger or smaller than the original, the image is stretched or shrunk to the requested size. If XPRINT RENDER is unsuccessful, an appropriate error is generated. The following code will retrieve the natural size of an image in a bitmap file, in pixels: nFile& = FREEFILE OPEN "myimage.bmp" FOR BINARY AS nFile& GET #nFile&, 19, nWidth& GET #nFile&, 23, nHeight& CLOSE nFile&
See also | XPRINT ATTACH, XPRINT COPY, XPRINT STRETCH


XPRINT SCALE statement

Purpose

Define a custom world coordinate system for the client (printable) area of the host printer page.

Syntax

XPRINT SCALE (x1!, y1!)-(x2!, y2!)

Remarks

XPRINT SCALE lets you define your own world coordinate system for all subsequent XPRINT statements. World coordinates may be values, with the only requirement that x1! not equal x2!, and y1! not equal y2!. If either pair are equal, an error 5 is generated. The custom coordinates remain with the printer page until XPRINT SCALE is repeated, or the host printer is detached. If executed without a host printer attached, error 57 is generated.

If x2! is greater than x1!, coordinates grow larger as they move to the right. Otherwise, they grow larger as they move to the left.

If y2! is greater than y1!, coordinates grow larger as they move downward. Otherwise, they grow larger as they move upward.

See also

XPRINT ATTACH

Example

' Attach the default Windows printer

XPRINT ATTACH DEFAULT

' Retreive the client size (printable area) of the printer page

XPRINT GET CLIENT TO ncWidth!, ncHeight!

' Retreive the resoltuion (points per inch) of the attached printer

XPRINT GET PPI TO x&, y&

' Width in inches of the printable area

ncWidth! = ncWidth!/x&

' Height in inches of the printable area

ncHeight! = ncHeight!/y&

' Set the scale to inches, for American letter-size paper

' in portrait mode. This is the equivalent to 8.5x11 minus the margins.

XPRINT SCALE (0,0)-(ncWidth!,ncHeight!)


XPRINT SET MIX statement

Purpose

Set the color mix mode for a host printer page.

Syntax

XPRINT SET MIX mode&

Remarks

Prior to any graphical operations, a host printer must first be selected with XPRINT ATTACH. There are 16 mix modes available to use for mixing the drawing color with the color that already exists at the drawing location. The default mix mode is 13, %mix_CopySrc. The mix mode equates are predefined in PowerBASIC.

%mix_Blackness | Pixel is always 0 (black).
%mix_NotMergeSrc | Pixel is the inverse of the MergeSrc color.
%mix_MaskNotSrc | Pixel is a combination of the colors common to both the pixel and the inverse of the source.
%mix_NotCopySrc | Pixel is the inverse of the pen color.
%mix_MaskSrcNot | Pixel is a combination of the colors common to both the source and the inverse of the pixel.
%mix_Not | Pixel is the inverse of the pixel color.
%mix_XorSrc | Pixel is a combination of the colors in the source and in the pixel, but not in both.
%mix_NotMaskSrc | Pixel is the inverse of the MaskSrc color.
%mix_MaskSrc | Pixel is a combination of the colors common to both the source and the pixel.
%mix_NotXorSrc | Pixel is the inverse of the XorSrc color.
%mix_Nop | Pixel remains unchanged.
%mix_MergeNotSrc | Pixel is a combination of the source color and the inverse of the pixel color.
%mix_CopySrc | Pixel is the source color (default).
%mix_MergeSrcNot | Pixel is a combination of the source color and the inverse of the pixel color.
%mix_MergeSrc | Pixel is a combination of the source color and the pixel color.
%mix_Whiteness | Pixel is always 1 (white).

See also

XPRINT ATTACH, XPRINT GET MIX


XPRINT SET ORIENTATION statement

Purpose | Set the paper orientation for a host printer page.
Syntax | XPRINT SET ORIENTATION orent&
Remarks | XPRINT SET ORIENTATION sets the orientation of the paper in the host printer. The value 1 indicates portrait mode, while 2 indicates landscape mode. If a host printer is not attached, or does not support setting the orientation, error 57 is generated.
See also | XPRINT ATTACH, XPRINT GET ORIENTATION


XPRINT SET PIXEL statement

Purpose | Set the color of a pixel on a host printer page.
Syntax | XPRINT SET PIXEL [STEP] (x!, y!) [, rgbColor&]
Remarks | XPRINT SET PIXEL draws a single pixel on the host printer page. The optional color parameter is an RGB value; if not included, the color defaults to the current foreground color for the host printer. If the STEP option is included, the x! and y! coordinates are relative to the last point referenced (POS). The coordinate points are specified in pixels, unless world coordinates were set with an XPRINT SCALE statement. If no host printer is attached, error 57 is generated.
See also | XPRINT ATTACH, XPRINT COLOR, XPRINT GET PIXEL


XPRINT SET POS statement

Purpose

Set the last point referenced (POS) by an statement.

Syntax

XPRINT SET POS [STEP] (x!, y!)

Remarks

XPRINT SET POS allows you to set the last point referenced (POS) by XPRINT statements. If the STEP option is included, the x! and y! coordinates are relative to the prior POS. The coordinate points are specified in pixels (or world coordinates, if those were defined with an XPRINT SCALE statement). If executed without a host printer attached, error 57 is generated.

See also

XPRINT ATTACH, XPRINT GET POS


XPRINT SET QUALITY statement

Purpose | Set the print quality for a host printer.
Syntax | XPRINT SET QUALITY qual&
Remarks | XPRINT SET QUALITY sets the print quality setting for the host printer. The value 1 is draft mode, 2 is low resolution, 3 is medium resolution, and 4 is high resolution. It should be noted that some printers only allow higher resolutions to be set from the printer dialog (in XPRINT ATTACH CHOOSE). If no host printer is attached, or the printer does not support print quality settings, error 57 is generated.
See also | XPRINT ATTACH, XPRINT GET QUALITY


XPRINT STRETCH statement

Purpose

Copy and resize a to a host printer page.

Syntax

XPRINT STRETCH hbmpSource???, id&, (x1!, y1!)-(x2!, y2!) TO (x3!, y3!)-(x4!, y4!) [, style%]

Remarks

You can copy a complete bitmap, or a portion of it, to the host printer page, while resizing it to a larger or smaller size. The expression hbmpSource??? specifies the handle of the source bitmap or window. The expression id& is reserved for future compatibility, and must always be set to zero. The destination of the copy operation is the host printer page. The bitmap is automatically resized to fit the destination parameters. You must use care that your parameters are valid for the specified bitmaps, or results of the operation are undefined.

If the style% parameter is included, it is one of the values in the following table. If not included, a default of %mix_CopySrc is presumed. There are 8 mix modes available to use for mixing drawing colors with the colors which already exist at the at the drawing location. The mix mode equates are predefined in PowerBASIC.

%mix_NotMergeSrc | Pixel is the inverse of the MergeSrc color.
%mix_NotCopySrc | Pixel is the inverse of the source color.
%mix_MaskSrcNot | Pixel is a combination of the colors common to both the source and the inverse of the pixel.
%mix_XorSrc | Pixel is a combination of the colors in the source and the pixel, but not in both.
%mix_MaskSrc | Pixel is a combination of the colors common to both the source and the pixel.
%mix_MergeNotSrc | Pixel is a combination of the pixel color and the and the inverse of the source color.
%mix_CopySrc | Pixel is the source color (default).
%mix_MergeSrc | Pixel is a combination of the source color and the pixel color.

A host printer must first be connected with XPRINT ATTACH. If a host printer is not attached, error 57 is generated.

See also

XPRINT ATTACH, XPRINT COPY, XPRINT RENDER


XPRINT STYLE statement

Purpose

Set the line style to be used by various statements.

Syntax

XPRINT STYLE linestyle&

Remarks

XPRINT STYLE determines the line style which will be used when drawing various graphical objects, while the width value is set to 1. When the width value is greater than one, Windows always interprets the style as 0 (solid).

Available line styles are:

0 | Solid (default)
1 | Dash
2 | Dot
3 | DashDot
4 | DashDotDot

See also

XPRINT ARC, XPRINT ATTACH, XPRINT BOX, XPRINT ELLIPSE, XPRINT LINE, XPRINT PIE, XPRINT WIDTH

Example

' Draw a square box with blue, dotted lines

XPRINT WIDTH 1

XPRINT STYLE 2

XPRINT BOX (10, 10) - (110, 110), 0, %BLUE


XPRINT TEXT SIZE statement

Purpose

Calculate the size of text to be printed on a host printer.

Syntax

XPRINT TEXT SIZE txt$ TO nWidth!, nHeight!

Remarks

This statement calculates the total size of the printed text, based upon the current font for the host printer. The sizes returned are specified in pixels, unless optional world coordinates have been defined with an XPRINT SCALE statement.

This allows you to easily calculate the appropriate print position, particularly when using a proportional font. If this statement is executed without a host printer attached, error 57 is generated.

See also

XPRINT, XPRINT ATTACH, XPRINT CHR SIZE, XPRINT FONT

Example

FUNCTION PBMAIN

' The following example draws the text both horizontally

' and vertically centered on the host printer page

LOCAL x, y, w, h, w2, h2 AS LONG

LOCAL sText AS STRING

sText = "PowerBASIC"

XPRINT ATTACH "Lexmark C750"

XPRINT COLOR %BLUE, -2 ' blue text, clear background

XPRINT FONT "Times New Roman", 18, 3 ' 18p, bold, italic

XPRINT GET CLIENT TO w, h ' get client size

XPRINT TEXT SIZE sText TO w2, h2 ' get text size

x = (w-w2) / 2 ' centered x-pos

y = (h-h2) / 2 ' centered y-pos

XPRINT SET POS (x, y) ' set position

XPRINT sText ' draw the text

XPRINT CLOSE

END FUNCTION


XPRINT WIDTH statement

Purpose

Set the graphic line width to be used by various statements..

Syntax

XPRINT WIDTH ncPixels&

Remarks

XPRINT WIDTH determines the line width which will be used when drawing various graphical objects. The default width is 1 pixel. The width is always specified in pixels, regardless of any XPRINT SCALE option. When the width is set to a value greater than 1, XPRINT STYLE parameters are always interpreted as 0 (solid).

See also

XPRINT ARC, XPRINT ATTACH, XPRINT BOX, XPRINT ELLIPSE, XPRINT LINE, XPRINT PIE, XPRINT STYLE

Example

FUNCTION PBMAIN

XPRINT ATTACH "Lexmark C750"

' Draw a square box with thick, red lines

XPRINT WIDTH 10

XPRINT BOX (10, 10) - (110, 110), 0, %RED

XPRINT CLOSE

END FUNCTION


Register

Register Now!

When you register PowerBASIC, you will receive early notification of product upgrades, new product announcements, special offers, a free subscription to the PowerBASIC Gazette newsletter and access to PowerBASIC's award winning technical support. Choose one of these easy options:

On-line: | Use your favorite Internet browser to visit our World Wide Web site at: http://www.powerbasic.com/ where you can quickly and easily register all of your PowerBASIC products using our on-line registration form.
By Fax: | Fill out the supplied registration card and fax it to +1 (941) 408-8820.
By Mail: | Fill out the supplied registration card and mail it to: PowerBASIC, Inc. 2100 Tamiami Trail S. Venice, FL 34293 USA

Don't forget to include your e-mail address! Upgrade notices and special offers are also sent out via electronic mail. Don't be the last to know about a new product release.


Technical Support

Before you take advantage of the wide range of support options available to you, be sure to check if we have already answered your question in your support materials:

  • The PowerBASIC documentation

  • The ONLINE HELP

  • The README files on the product disks

  • The TECHNOTES section on the PowerBASIC web site

  • The FAQ section of the PowerBASIC web site.

  • The Peer Support forums on the PowerBASIC web site

Be sure to visit our home page for the latest news, information on upgrades, free subscriptions to the PowerBASIC Gazette, and programming tips.

Example files can be downloaded from the FILES section of the PowerBASIC web site at http://www.powerbasic.com/support/downloads/

Answers to frequently asked questions and other programming tips are regularly made available on our web site in the Support Center.

Be sure to add the PowerBASIC web site to your bookmark list!

See Also

Support Options


Support Options

If you are still unable to resolve your question, there are several support options available, including Peer Support, Installation Support, and Priority Support.

Peer Support: Peer Support is available through the free PowerBASIC Peer Support Forums on our World Wide Web site and BASIC programming newsgroups in the public Usenet feed. Thousands of PowerBASIC programmers read and respond to messages in these forums every day, and the forums are accessible 24 hours a day, seven days a week. There are tens of thousands of searchable messages, with plenty of source code! We strongly encourage to you participate in the discussions that take place there!

IMPORTANT: When sending messages about PowerBASIC in a public forum, do not reveal your product serial number or credit card information. This information should only be given to PowerBASIC employees via private electronic mail, fax, or telephone.

Installation Support: Installation Support (questions about PowerBASIC installation and initial setup) is free to all Registered users, via e-mail, telephone and fax.

Priority Support: Priority Support is available for questions that are not covered by Installation Support. Priority Support can be charged to your credit card. Subject to certain restrictions, you may choose between Advisory Help and Per Incident Help:

Advisory help is available at $49 USD per incident. Advisory Help can include anything from beginner-level usability questions to advanced consulting-level application design questions.

Per Incident help is available at $49 USD for each incident. An incident is defined as a single support issue and the effort needed to resolve it. A single support issue is a problem, which cannot be broken down into smaller problems. Before an incident report is opened for a customer, the customer and the support engineer must agree on what the problem is and the parameters for an acceptable solution. It may be necessary to separate complex problems into multiple incidents.

Incident support typically involves off-line research by a support engineer and may require multiple telephone calls, faxes, or e-mail messages.

If you have paid for Priority Support and the problem is attributed to a confirmed bug in your PowerBASIC compiler, the Priority Support fee will be refunded or you can place it on account for future use.

You can telephone our Technical Support department at +1 (941) 408-8700, Monday to Friday between 10:00 am and 5:00 pm Eastern Time (except holidays). If you are not already registered, a customer service representative will register you before you are transferred to a support engineer. You will not be charged for the registration process time.

You can fax our Technical Support department at +1 (941) 408-8820. Be sure to include a detailed description of your problem, including any code fragments that may be necessary. Don't forget to include your return fax telephone number (including country code if outside the U.S. and Canada).

You can send E-mail to our Technical Support department at support@powerbasic.com. Be sure to include a detailed description of your problem, including any code fragments that may be necessary. Don't forget to include your return e-mail address and your Serial Number.

IMPORTANT: PowerBASIC, Inc. reserves the right to change prices at any time. For up to date pricing on all products and support options, please contact PowerBASIC, Inc.


Additional Source Code and Examples

Additional source code, example files, and utilities can be found on the PowerBASIC web site. Click the following links to go to those web pages:

  • The FILES section on the PowerBASIC web site

  • The Source Code forum in the free PowerBASIC Peer Support Forums on the PowerBASIC web site

See Also

Add-on Libraries, Utilities and Tools

WIN32API.INC Updates


Add-on Libraries, Utilities and Tools

Third Party libraries, utilities, and tools can be found on the Add-on's section on the PowerBASIC web site.

See Also

Additional Source Code and Examples

WIN32API.INC Updates


WIN32API.INC Updates

The WIN32API.INC (and the related .INC files) are frequently updated by PowerBASIC, and these updated files are provided as a courtesy service to PowerBASIC programmers.

The latest edition of the WIN32API.INC files can be downloaded from the FILES section of the PowerBASIC web site.


Appendix D - Year 2000 compliance

This PowerBASIC Compiler is Year 2000 compliant.

The BASIC programming language, as defined by PowerBASIC, currently supports just one date related keyword: DATE$.

The DATE$ system variable allows a program to retrieve the operating systems current date or change the system date. The system date is the value maintained by your computer's hardware.

Using the syntax:

A$ = DATE$

a program can retrieve the current system date. The date is always returned using a 4-digit year, such as "01-01-2004".

Using the syntax:

DATE$ = "01-01-2004"

a program can change the system date. Most BASIC dialects, including PowerBASIC, also allow a two-digit year to be used when changing the system date. When used in this fashion, most BASICs assume the century 1900. In PowerBASIC, all two-digit years less than 80 are assumed to be in the century 2000.


Appendix A - License Agreement

License Agreement

This software is protected by United States copyright law and international treaties. It is licensed for use by one specific person, whose name will be registered with PowerBASIC, Inc., on a single computer system. It may be moved from one computer to another as long as there is no possibility of it being used on two computer systems at the same time. If this software is used on a network, one licensed copy of the software is required for each person who uses the software. If the licensed product is a compiler, you may distribute the programs you create royalty free. You may not distribute the licensed compiler. If the licensed product includes one or more runtime modules, you may reproduce and distribute them royalty free, provided they are distributed only in conjunction with, and as part of your software program, and provided that they bear your copyright notice or the copyright notice which appears on the PowerBASIC, Inc. disk label. The runtime modules are those files that are required to execute your software program, and which are specifically designated as "runtime modules" in the accompanying PowerBASIC, Inc. documentation. Your use of any of the demonstration or sample programs provided with this product are governed by the notices and restrictions of the respective author or copyright holder. Except as stated above, you may not rent, lease, or lend any part of the licensed software.

LIMITED WARRANTY

PowerBASIC, Inc. warrants that the physical disks and physical documentation are free of defects in workmanship and materials for a period of sixty days from the date of purchase. If the disks or documentation are found to be defective within the warranty period, PowerBASIC, Inc. will replace the defective items at no cost to you. The entire liability of this warranty is limited to replacement and shall not, under any circumstances, encompass any other damages.

PowerBASIC Inc. specifically disclaims all other warranties, expressed or implied, including, but not limited to, any implied warranty of merchantability or fitness for a particular purpose.

GOVERNING LAW

This license and limited warranty shall be construed, interpreted, and governed by the laws of the State of Florida, and any action hereunder shall be brought only in Florida. If any provision is found invalid or unenforceable, the balance of this license and limited warranty shall remain valid and enforceable. Use, duplication, or disclosure by the U.S. Government of the computer software and documentation in this product shall be subject to the restricted rights under DFARS 52.227-7013 applicable to commercial computer software. All rights not specifically granted herein are reserved by PowerBASIC, Inc.

If you have any questions concerning this license agreement, please contact our support department by email at support@powerbasic.com, by fax at +1 (941) 408-8820 or by telephone at +1 (941) 408-8700.

See Also

Year 2000 compliance


Key(1)

"*" indicates a C/C++ pointer. For example, char* MyString indicates a variable MyString that points to a char value.


Key(2)

Denotes the closest matching data type, based on usage in modern compilers. In the case of the bool type, older MS C++ compilers may substitute a 32-bit unsigned integer (LONG).


Key(3)

In 16-bit C/C++ code, a UINT is a 16-bit variable (WORD). Likewise, an int in 16-bit C/C++ code is a 16-bit variable (INTEGER).


Key(4)

The LP prefix commonly denotes a pointer variable. For example, LPRECT would be a pointer to a RECT structure or UDT.


key(5)

In 16-bit Delphi code, the data type has a 16-bit width. In 32-bit Delphi code, the data width is 32-bits.


Key(S/U)

Signed or Unsigned, depends upon the code context or platform.


Key DefType

DEFtype refers to all variable type declaration statements, with the exception of ASCIIZ strings, fixed-length strings, and . The DEFtype statement may not be supported in future editions of PowerBASIC, so use explicit declarations wherever possible to maintain future compatibility.


Key String

For dynamic strings, only the string handle (a DWORD) is contained in a string array element. The string data itself is stored elsewhere in memory; it occupies as many bytes as the string has characters, plus a trailing CHR$(0) or $NUL, although this extra byte is completely transparent to the programmer.


Programming Reference

Welcome to the Programming Reference guide. The following chapters in the documentation are designed to provide an in-depth discussion and tutorial on some of the more advanced aspects of programming using the PowerBASIC Console Compiler.

Please select from the following topics:

  • Graphics

  • Printing

  • COM Programming Introduction

  • Files

  • Serial Communications

  • TCP and UDP Communications

  • The Inline Assembler

  • Variables and Variable Scope

  • Errors and Error Trapping

  • Resource Files