Professional Documents
Culture Documents
PRGV2
PRGV2
Limitation of liability This document is provided “as-is”. Information and views expressed in this document, including
URL and other Internet Web site references, may change without notice. You bear the risk of using
it.
Some examples depicted herein are provided for illustration only and are fictitious. No real
association or connection is intended or should be inferred.
Intellectual property This document does not provide you with any legal rights to any intellectual property in any
Microsoft product.
You may copy and use this document for your internal, reference purposes.
Trademarks Microsoft, ActiveX, Dexterity, Excel, Microsoft Dynamics, Outlook, Visual Basic, Visual SourceSafe,
and Windows are trademarks of the Microsoft group of companies. FairCom and c-tree Plus are
trademarks of FairCom Corporation and are registered in the United States and other countries.
Warranty disclaimer Microsoft Corporation disclaims any warranty regarding the sample code contained in this
documentation, including the warranties of merchantability and fitness for a particular purpose.
License agreement Use of this product is covered by a license agreement provided with the software product. If you
have any questions, please call the Microsoft Dynamics GP Customer Assistance Department at
800-456-0025 (in the U.S. or Canada) or +1-701-281-6500.
ii P R O G R A M M E R ’ S G U I D E V O L U M E 2
C O N T E N T S
iv P R O G R A M M E R ’ S G U I D E V O L U M E 2
C O N T E N T S
vi P R O G R A M M E R ’ S G U I D E V O L U M E 2
C O N T E N T S
viii P R O G R A M M E R ’ S G U I D E V O L U M E 2
C O N T E N T S
x P R O G R A M M E R ’ S G U I D E V O L U M E 2
C O N T E N T S
Index............................................................................................................................................... 473
• Part 2, Basic Scripting, describes how to begin writing scripts for your
Dexterity application.
• Part 3, Working with Controls, explains how to work with the various
user interface controls available in Dexterity.
2 P R O G R A M M E R ’ S G U I D E V O L U M E 2
IN TRO DUCT IO N
Symbol Description
➥ of table Items; A continuation character indicates that a script
continued from one line to the next should be
typed as a single line in the Script Editor.
The light bulb symbol indicates helpful tips,
shortcuts and suggestions.
Convention Description
6 P R O G R A M M E R ’ S G U I D E V O L U M E 2
Chapter 1: Syntax
To make your application work properly, you need to apply logic and rules
that determine how the application will function. The Dexterity
development system has its own programming language, sanScript, that
you’ll use to control how your application operates.
Unlike many traditional languages that require you to write long pieces of
program code, sanScript is written in small segments, or scripts, that are
attached to objects in an application dictionary, such as fields, menus,
windows and forms. Scripts are run as the user interacts with that
particular object in the application. For example, a script applied to a push
button will run when the user clicks the button.
• General syntax
• Names
• Statements
• Functions
• Data types in sanScript
• Variables
• Constants
• Expressions
• Operators
• Order of precedence
• Casting
• Arrays
General syntax
SanScript is a relatively easy language to learn, but you must follow some
basic rules when you write scripts.
Names
In a script, you’ll refer to the names of objects such as fields, windows or
forms in an application dictionary. Depending on the object referenced in
the script, you must indicate either the object’s simple name or qualified name.
Simple names
A simple name is the name you give an object when you create it. When
you use a simple name, sanScript assumes that the object is part of the
current window where you’re attaching the script. This allows the same
object to be used in multiple places.
8 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 1 S Y N T A X
A simple name must start with a letter, and can contain up to 78 additional
letters, digits, underscores or spaces for a total size of 79 characters. A
simple name that has spaces in it must be surrounded by single quotation
marks when referenced in a script.
Because names must be surrounded with single quotation marks if they contain
spaces, we recommend that you substitute underscores for spaces in names when
you name objects in your application. This makes them easier to use in scripts.
Single and double quotation marks are used differently in scripts. Single quotation
marks indicate the names of objects, while double quotation marks indicate the item
between the quotation marks is a string value.
Qualified names
A qualified name explicitly specifies the location of the object being
referenced. Qualified names consist of a qualifier, which specifies the
location of the object, and a simple name. Qualified names are used to
reference objects that aren’t part of the current window or to resolve name
conflicts. For example, to refer to the Customer Name field in the
Customer_Master table instead of the Customer Name field in the current
window, you would qualify the field with the of table qualifier as shown in
the following script.
• When the script refers to a field in another window of the current form,
use the following syntax:
• When the script refers to a field in a window that is not part of the cur-
rent form, use this syntax:
When you use qualified names containing spaces in the name, the “of”
portion of the name shouldn’t be inside the single quotation marks. The
following name is valid:
Local fields have a special qualifier that indicates the field is a local
field. When you reference a local field in a script, the letter L in
parentheses – (L) – must be included before the name of the field, with the
entire name enclosed in single quotes. For example, the following name for
the Customer Type local field is valid:
10 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 1 S Y N T A X
Statements
Statements are a type of command used in sanScript to complete a specific
action in your application, such as opening a window, saving an item in a
table or displaying a message for the user. For instance, to display a
message in your application you can use the error statement:
Functions
Functions are commands similar to statements, but unlike statements, they
return a value that is then used by another portion of the script. For
example, the countitems() function is used to count the number of entries
in a list box field displayed in a window. The name of the list box field is the
function’s parameter while the number of items displayed in the list box is
the value returned. The following example sets the variable “number” to
the number of items in the list box named Shipping Methods.
Refer to the Function Functions such as countitems() are built into sanScript. The sanScript
Library Reference language also has dozens of functions that are contained in the function
manual for more libraries. The function libraries are groups of special-purpose functions that
information about extend the capability of Dexterity. For instance, the WinHelp function
function libraries. library contains functions used to implement an online help system for a
Dexterity application.
You can also create your own functions for use in scripts. Refer to Chapter
21, “User-defined Functions,” for more information.
12 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 1 S Y N T A X
String and text data types are stored as Pascal strings in a data file. Pascal
strings use a length byte (1 byte for strings, 2 bytes for text) to store the
length of the field. Typically, these data types must also have an even
storage size. If the sum of the storage size and length byte is an odd
number, a pad will be used when you set the storage size for the data type.
Even storage sizes are required for microprocessor compatibility.
The other control types not mentioned in the previous table either don’t
store information or store information in a format unique to the control
type. Push button, radio button, reference, tree view and list view control
types don’t store information. Composite, multi-select list box and picture
control types each store data in their own unique format.
Converting data
When working with fields in scripts, it may be necessary to convert how the
data is stored to a different storage method. This conversion can happen
implicitly or explicitly.
You can also use casting to convert values from one type to another. Refer to
Casting on page 23 for more information.
Variables
Variables allow an application to temporarily store values used by the
application. Dexterity has two types of variables: local variables and global
variables.
Local variables
Local variables are specific to a single script and are active only while the
script is running. They’re used to store intermediate values, such as the
resulting value of an expression, while a script is running. To create local
variables, define them at the beginning of the script they’ll be used in. The
word local, followed by the type of variable (integer, long, currency,
vcurrency, string, time, boolean) and the variable name are required for
each local variable.
Note that you use the word long to indicate a long integer type in scripts. Use the
word vcurrency to indicate a variable currency type in scripts.
When you declare a local variable, you can specify its initial value. This is
shown in the following example.
Global variables
For more information Global variables are active the entire time a Dexterity application is open, so
about creating global they’re available to any script in the application dictionary, at any time.
variables, refer to Global variables are used to store information that affects the entire
Chapter 22, “Global application, such as whether printing is currently allowed. Global variables
Variables,” in Volume 1 are created in the Resource Explorer. To reference global variables in a
of the Dexterity script, the qualifier of globals must appear after the name of the global
Programmer’s Guide. variable.
14 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 1 S Y N T A X
Constants
Constants are fixed numeric or string values that are used in scripts, where
the constant name is used in place of the value associated with it. Two types
of constants are available in Dexterity: Dexterity constants and user-defined
constants.
Dexterity constants
Refer to Chapter 21, Dexterity has a number of predefined constants that can be used in scripts.
“Constants,” in Volume For example, the three constants listed in the following table are used with
1 of the Dexterity the ask() function.
Programmer’s Guide
for a list of special Constant name Description
predefined constants ASKBUTTON1 The value returned from the ask() function when the first button
in Dexterity. is clicked in the ask dialog box.
ASKBUTTON2 The value returned from the ask() function when the second
button is clicked in the ask dialog box.
ASKBUTTON3 The value returned from the ask() function when the third
button is clicked in the ask dialog box.
User-defined constants
Refer to Chapter 21, You can also create your own constants using the Constant Definition
“Constants,” in Volume window. For example, you could define a constant named “PAY_PERIODS”
1 of the Dexterity to have the value 24, representing the number of pay periods in a year. To
Programmer’s Guide calculate an employee’s salary in a script, you can use the constant in place
for information about of the number.
how to create
user-defined constants. 'Check Amount' = Salary / PAY_PERIODS;
You can define global constants or form-level constants. Global constants can
be accessed by any script in your application. You should use them when
you anticipate a constant will be used by scripts throughout the application.
Within the scope of a form, you can reference a form-level constant directly.
To reference a form-level constant from outside the scope of a form, use the
qualifier of form form_name. If a form-level constant and a global constant
have the same name, by default, scripts attached to the form will use the
form-level constant. To refer to the global constant instead, use the qualifier
of globals following the name of the constant. For example, the following
statement refers to the BUNGALOW constant defined on the Houses form.
Expressions
An expression is a sequence of operands and operators that are evaluated to
return a value. Operators indicate the type of procedure to perform on the
operands. The operands are usually items in the application dictionary,
such as the content of fields or the values returned from functions or other
expressions. In sanScript, there are four kinds of expressions: numeric, date
and time, string, and boolean.
Numeric expressions
Evaluating a numeric expression results in numeric value. For instance, the
following sanScript statement uses a numeric expression that returns the
total price of a sale by adding the subtotal and sales tax fields.
16 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 1 S Y N T A X
The following table lists the numeric operators in the order they’re
evaluated.
The following table lists the date and time operators in the order they’re
evaluated.
When comparing time values, be aware that the seconds portion of the time value is
ignored.
String expressions
Evaluating a string expression results in a string value. For instance, the
following sanScript statement joins two string values and stores the result
in the Name field.
The following table lists the string operators in the order they’re evaluated.
Boolean expressions
Evaluating a boolean expression results in boolean value of true or false.
Boolean expressions are used in decision-making statements such as the if
then...end if statement. For instance, the following sanScript statement
evaluates the boolean expression in the if then...end if statement to
determine whether the value of the Number of Users field is greater than 5.
The following table lists the boolean operators in the order they’re
evaluated.
18 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 1 S Y N T A X
Operators
This section lists the operators supported in sanScript. Examples show how
each operator is used in the different types of expressions.
20 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 1 S Y N T A X
Order of precedence
The order of precedence is the order in which the operations are carried out
for an expression. The order of precedence for sanScript is:
The first expression performs a bit-wise and operation on the two integer
values and then compares the result to 0. The second expression compares
the second integer value to 0 and then performs a bitwise and operation on
that boolean result and int_val1. The second expression can’t be compiled
because sanScript can’t perform a logical and operation on a boolean and an
integer.
22 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 1 S Y N T A X
Casting
A cast allows you to explicitly change the type of an expression. Casts are
used when you know the type of expression you need, and want the
compiler to carry out the conversion. Use the following syntax to perform a
cast:
type(expression)
type – Specifies the resulting type of the expression. This can be integer,
long, currency, vcurrency, string or boolean.
Some casts are not permitted. For example, you can’t use a cast to convert a
currency expression into a date expression. Invalid casts will compile, but
will generate errors at runtime.
long_int_val = long(15);
cur_val = currency(value("45.12"));
Arrays
Refer to Chapter 8, The individual pieces that make up an array are called elements. The
“Global Fields,” in elements are numbered from 1 to the size of the array. Arrays can have up
Volume 1 of the to 32,767 elements. The number used to indicate a specific element of the
Dexterity array is called the array index. A specific element of the array is referenced in
Programmer’s Guide sanScript using its corresponding array index.
for more information
about creating array An array can be used in place of several fields that have the same data type
fields. and will be storing similar information. For example, if an application
keeps track of monthly sales for a year, one monthly sales array field with
an array size of 12 could be used to store this information instead of 12
separate fields.
Arrays in Dexterity
Dexterity applications can use array fields and array local variables. Array
fields are created in the Field Definition window by setting the Array Size
field to the size of the array. Array local variables are created by including
the size of the array in brackets – [ ] – following the local variable name.
When creating local arrays, the array size must be a constant expression.
Array sizes can’t be set at runtime.
Using arrays
To access the elements of an array from within a script, simply use the name
of the array and add the index number of the element you want to
reference. Be sure the index number is included in square brackets after the
name of the array and before the qualifier. The following example sets the
third element of the Quarterly Sales array field (corresponding to the third
quarter) to the sum of the monthly sales for July, August and September.
The following example uses a local array field with five elements to act as
status flags for the script. The for loop initializes the flags to false.
for i = 1 to 5 do
status[i] = false;
end for;
24 P R O G R A M M E R ’ S G U I D E V O L U M E 2
Chapter 2: The Script Editor
Dexterity has a built-in script editor you can use to write scripts for your
application. The Script Editor operates like an ordinary text editor, allowing
you to enter a script. You can also use the Script Editor to save and compile
scripts. Information is divided into the following sections:
Edit text in the Script Editor the same way you would with any other editor.
You can cut, copy and paste text by choosing the corresponding commands
from the Edit menu. You can also indent a block of text by selecting the text
and pressing the TAB key. To remove an indent from a block of text, select
the text and press SHIFT-TAB.
Button Description
Saves the script source to the application dictionary and attempts
to compile the script.
Closes the current script. If only one script is open, the Script
Editor window will be closed. If the script has changed, you will be
asked whether to compile and save changes.
Prints the current script.
26 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 2 T HE S C R I PT E D IT O R
Syntax coloring
To make scripts easier to read, color coding is applied to the various
elements of the syntax. For example, keywords are shown in one color,
while comments are shown in another. Use the Options window to specify
the colors used for the various syntax elements.
Syntax coloring can be used in both the Script Editor and the Script
Debugger. You can also specify the font and size of the text used for scripts.
At the top of the hierarchy are the forms, tables, and global resources in the
dictionary. Below that are the resources that are found within the context of
the top-level resource. For example, within the context of a specific form
there is a list of the windows, procedures, functions, commands, and
constants for that form. The Names window follows this hierarchy when it
displays the names of resources in the dictionary. The Names window is
shown in the following illustration.
28 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 2 T HE S C R I PT E D IT O R
Global resources
To use the Names window to access the names of global resources,
complete the following procedure:
After you click OK, the Names window will close and the Script Editor
window will be displayed. Place the insertion point where you want the
name to appear in the script and choose Paste from the Edit menu. The
name will be added to your script with its simple name or, if necessary, its
qualified name.
The first time you open the Names window, it will attempt to set the context
based on the resource you’re attaching the script to. For example, if you’re
attaching a script to a window field, the Names window will use the
context of the current form and window. From that point, the Names
window remembers the settings from the last time it was accessed.
Script menu
The Script menu is available only when the Script Editor window is open.
The items in this menu allow you to search and replace text in the script.
You can also check the syntax of the script, save the script source, and drill
down to function and procedure scripts. The following table lists the items
in the Scripts menu.
The Find and Replace menu options search only from the current insertion point
position to the end of the script. To ensure the entire script is searched, move the
insertion point to the beginning of the script before choosing Find or Replace.
30 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 2 T HE S C R I PT E D IT O R
Script drill-down
For information about Script drill-down allows you to view and switch between multiple
procedures, refer to procedures, user-defined functions, form-level procedures and form-level
Chapter 20, user-defined functions, without having to leave the Script Editor. This is
“Procedures.” For useful when you want to examine a progression of procedures or functions
information about called from a script.
user-defined functions,
refer to Chapter 21, Opening a script
“User-defined To use the script drill-down feature, highlight the name of a procedure,
Functions.” user-defined function, form-level procedure or form-level user-defined
function in a script, and choose Open Script from the Script menu. The
procedure or user-defined function will be displayed in the Script Editor
window.
To open a procedure or
user-defined function,
highlight its name and
choose Open Script from
the Script menu.
The script name will also be added to the Scripts List. (The Scripts List can
be displayed by pressing the button labeled with the small script icon
located on the right side of the Script Editor window.)
Closing a script
To close a script, choose it from the Scripts List so it appears in the Script
Editor window, then click Close. If the script has changed, you will be asked
whether you want to save the changes and compile the script.
Compiling scripts
A complete list of After you write a script, the script must be compiled into a form that can be
compiler messages used at runtime. Dexterity has a built-in compiler that will compile a script
can be found in the when you click Compile. If you want to save the script without compiling
online help. it, choose Save Source from the Script menu.
Compiler messages
The compiler in Dexterity returns two types of messages. Compiler errors
are problems in a script that prevent it from being compiled, such as syntax
errors. Compiler warnings are less serious problems that won’t prevent a
script from being compiled, but should be resolved. An example of a
compiler warning is a local variable that is declared, but not used.
32 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 2 T HE S C R I PT E D IT O R
You can also display compiler messages in the Compile Messages window,
shown in the following illustration.
When the Script Editor window is open, The Compile Messages window
shows the errors and warnings for the current script. If the Script Editor is
not open, the Compile Messages window lists all of the compiler messages
from the last scripts that were compiled.
If you would like to compile all scripts in your dictionary, click the Compile
Dictionary button in the Resource Explorer toolbar. A progress indicator
will be displayed, showing the progress of the compile operation.
34 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 2 T HE S C R I PT E D IT O R
If a help entry can’t be found for the text you have selected, the help system
will attempt to find help for an item in the current line of the script. If no
help can be found, the Script Editor will beep.
Naming scripts
Dexterity automatically enters script names for scripts when the Script
Editor opens. These names closely follow the naming conventions used for
products like Microsoft Dynamics™ GP, and are based upon the type of
script and where it’s attached. You can use the automatically-generated
names or your own script names.
The following table describes the conventions used for the automatically-
generated script names. The descriptions use the following standards:
• Italicized text indicates items that are replaced by other names or values.
36 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 2 T HE S C R I PT E D IT O R
• Field scripts
• Window scripts
• Form scripts
• Other scripts
Field scripts
To apply a script to a field, such as a push button or a string field, display
the layout window for the window containing the field. Use the arrow tool
to select the field. Display the Properties window and click the Script tab;
the script types for the field will be displayed.
In the properties list, select the type of script to apply and click the lookup
button. The Script Editor window will open. After you’ve saved the script,
its name will appear next to the type in the properties list.
Double-clicking the script type in the properties list will also open the Script Editor
window.
The script type you choose depends on the field you’re attaching a script to
and when you want the script to run.
Field pre script This script runs when the focus (the location where the
user currently can enter information) moves to the field from another
location in the window.
Field change script This script runs if the value of the field has
changed and the focus moves out of the field. Typically, most of an
application’s scripts are change scripts.
In fields that toggle, such as push buttons or visual switches, the change
script runs as soon as you click the field. For instance, a push button may
use the following change script to open a window:
In editable fields, such as string or integer fields, the change script runs
when the focus moves from the field and a change is detected. (Dexterity
makes a comparison to the original value of the field to determine whether
the contents of the field changed.)
For tree view fields, this is called the select script. List view fields have the
selection changed and the item changed scripts that run when the selection
of the list field has changed.
Field post script This script runs when the focus leaves the current
field, regardless of whether the value of the field has changed.
ContextMenu script This script runs when the user right-clicks on the
current field. It is used for context menus.
MouseEnter script This script runs when the mouse pointer moves
over the current field.
Avoid using MouseEnter scripts on combo boxes, composites, drop-down lists, and
radio buttons.
MouseExit script This script runs when the mouse pointer moves off of
the current field.
Avoid using MouseExit scripts on combo boxes, composites, drop-down lists, and
radio buttons.
40 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 3 A TT A C H I N G S C R I P T S
Collapse script This script is available only for tree view fields. It runs
when a node of the tree is collapsed.
Expand script This script is available only for tree view fields. It runs
when a node of the tree is expanded.
Double click script This script is available only for tree view and list
view fields. It runs when the user double-clicks a node of the tree or double-
clicks an item in the list.
State click script This script is available only for tree view and list
view fields. It runs when the user clicks the state image for a node of the
tree or an item in the list.
Window scripts
To apply scripts to a window, open the layout for the window. Select the
window by clicking somewhere outside of the window area. The window is
selected when resize handles appear on the window’s perimeter. Display
the Properties window and click the Script tab; the script types for the
window will be displayed.
In the properties list, select the type of script to apply and click the lookup
button. The Script Editor window will open. After you’ve saved the script,
its name will appear next to the type in the properties list.
Window pre script This script runs just as the window is opened.
Typically, window pre scripts can be used to set default values in the
window, or to disable buttons or specific fields before the window is
displayed. Window pre scripts also run when the restart form or restart
window statements are used.
For example, in windows that contain a Delete button, you may want to
disable the button in the window pre script because the window contains
nothing to delete when it opens.
When a record has been retrieved that the user can delete, the Delete button
can be enabled by another script.
Window post script This script runs just as the window is closed. For
example, a window post script can be used to ascertain whether the entries
in the window have been saved to a file when the user closes the window. If
the information hasn’t been saved, the script could display a message
asking the user whether he or she wants to save changes.
Window print script This script runs when the window is active and
the user chooses the Print menu item. Typically, the window print script
uses the run report statement to run a report already created using the
Report Writer.
For example, the following print script prints an invoice when the Invoice
Entry window is active.
Window activate script This script runs each time the window
becomes active, such as when it’s opened or brought to the front after the
user has stopped working with another window. The window activate
script runs after the window pre script.
The window activate script must be used cautiously. It should not contain any
commands that create dialog windows (such as messages with the error or
warning statements) because the application will become suspended in an endless
loop.
ContextMenu script This script runs when the user right-clicks on the
window. It is used for context menus.
42 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 3 A TT A C H I N G S C R I P T S
Form scripts
To apply scripts to a form, open the form by clicking the Forms button on
the toolbar and opening the Form Definition window for the form you want
to attach scripts to. In the Form Definition window, click the Scripts tab. Use
the Pre Script and Post Script buttons to attach scripts to the form.
Form pre script This script runs just as the form is opened. A form is
opened when a window in the form is opened or when the open form
statement is executed. Typically, form pre scripts are used to set initial
characteristics of windows in the form. The form pre script doesn’t run
when a form is restarted.
Form post script This script runs just as the form is closed. A form is
closed when the main window for the form is closed, or when the close
form statement is executed. Typically, form post scripts are used to set
values in other areas of your application based on values entered in the
windows of the form.
Other scripts
You can use other types of scripts in your application that are described in
other locations:
• Chapter 12, “Tree View,” describes attaching scripts to tree view fields.
• Chapter 13, “List View,” describes attaching scripts to list view fields.
46 P R O G R A M M E R ’ S G U I D E V O L U M E 2
Chapter 4: Writing scripts
To make your application perform useful work, you need to add scripts.
You should create most of the forms, fields, tables and windows in your
application before writing and attaching scripts, because you’ll be
referencing these items from within scripts. Information about creating
scripts is divided into the following sections:
• Scripting overview
• Interaction-based programming
• Procedure: Creating scripts
Scripting overview
Scripts are the essential element that allow an application to perform useful
work. In Dexterity-based applications, scripts perform three basic
functions:
• Performing calculations
• Controlling the user interface
• Working with data in tables
While these are the basic functions a script can have, any particular script
may perform one or more of them.
Performing calculations
In almost all cases, applications written with Dexterity perform some type
of calculations based on data supplied by the user or previously stored in
tables. Chapter 6, “Performing Calculations,” describes special situations
you should be aware of when performing calculations in your application.
Interaction-based programming
In traditional procedural languages, like Pascal or BASIC, programs
typically consist of a main routine that runs when the program starts. This
routine may issue program calls to other routines. When the main routine
has finished, so has the program. Any interaction between the program and
the user is restricted. The user can interact with the program only when the
program allows it.
The next section contains a general procedure you can use when creating
scripts for Dexterity-based applications. This procedure attempts to handle
the “interaction” aspects of scripting, as well as the “functional” aspects.
48 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 4 WR I T IN G S C R IP T S
• Look for problems the script might encounter, such as having incor-
rect data, or not finding data at all. Think of navigational problems
as well; for instance, the user might close the window before enter-
ing all the necessary data. Think of actions the user could take to
“upset” the script.
50 P R O G R A M M E R ’ S G U I D E V O L U M E 2
Chapter 5: Communicating With the User
The interface you provide for your application is the primary method the
user will have to interact with it. Various sanScript commands are available
to help your application interact with the user. Information about them is
divided into the following sections:
• Dialogs
• Controlling forms and windows
• Controlling fields
• Controlling the focus
Dialogs
Several statements and functions allow you to communicate with the user.
The following are some of the methods you can use.
You may want to display multiple lines within a dialog box. To have a
multi-line message where you control the line breaks, you need to use a
local text field as the source of the message text. When a text value is
displayed in a dialog box, Dexterity will automatically add a scroll bar and
allow multiple lines to be displayed. You can use the char() statement to
indicate a new line.
If the value of the local variable answer is 1, then the user clicked Yes. If the
value is 2, the user clicked No, and if it’s 3, the user clicked Cancel. You can
use the ASKBUTTON constants when examining the return value.
52 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 5 C O M M U N IC AT IN G WI T H T H E U S E R
You need to open the form before you can open any of the windows that are part of
the form.
The close form statement closes a form. The following script closes the
House_Lookup form.
The close window statement closes windows. The following script closes
the Customer_History window.
Controlling fields
Once a window is open and visible to the user, you can use scripts to
control how the fields appear in the window. The following commands
show some of the actions scripts can perform for fields.
The show field statement makes a previously invisible field visible and
accessible to the user. The following script makes the Sales Tax field of the
current window visible.
The unlock statement makes a previously locked field accessible to the user.
The following script unlocks the Sales Tax field.
54 P R O G R A M M E R ’ S G U I D E V O L U M E 2
Chapter 6: Performing Calculations
Scripts are used to perform all of the calculations in your application.
Information about performing calculations is divided into the following
sections:
• Basic calculations
• Overflow in numeric expressions
• Calculations with variable currency values
• Bit operations
• Hexadecimal values
• String calculations
Basic calculations
To perform basic numeric calculations in a script, simply use a numeric
expression. For example, the following script computes the average utility
bill for a three-month period.
For example, multiplying the integers 450 and 75 should result in the value
33,750, but instead results in -31,786. Because the actual result is larger than
32,767, the maximum amount that can be represented by an integer in
Dexterity, overflow occurs.
To avoid the overflow, the two integer values must also be converted to
long integers as shown in the following script:
operand1 = 450;
operand2 = 75;
product = operand1 * operand2;
You can perform calculations with variable currency values much like you
perform calculations with standard currency values. All currency functions
such as round() and truncate() work properly with variable currency
values.
56 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A PT E R 6 P ER FO R M I N G C AL C U L A TI O N S
Rounding
When performing calculations with currency or variable currency values,
the final results often must be rounded. You can use the round() function to
round individual values. You can also use the default roundmode to
statement to specify the default rounding characteristics for all calculations
in a specific script.
Bit operations
Dexterity includes several functions that allow you to perform bit-wise
operations on integral values. An integral value is one that doesn’t have a
fractional portion, such as an integer. Use the following functions to clear,
set and test the status of a bit:
• bitclear()
• bitset()
• bittest()
Refer to the SanScript Reference manual for more information about using
these functions.
Hexadecimal values
Refer to the SanScript In sanScript, tiny integer, integer and long integer values can be represented
Reference manual for in hexadecimal form. To indicate a hexadecimal value, use one of the
more information following forms:
about the hex() and
value() functions. &hnnnnnnnnn
0xnnnnnnnnn
int_value = &hFF;
You can convert values between the hexadecimal representation and the
decimal equivalent. The hex() function returns a string containing the
hexadecimal equivalent of an integral value. The value() function can be
used to return the integral equivalent of a string containing a hexadecimal
value.
String calculations
Calculations involving strings and string values are also common in
Dexterity-based applications.
Appending strings
Appending strings together is a common sanScript operation. For example,
the following code appends the Company Name variable into a string.
String commands
Several sanScript commands are available to work with strings. Some
commands retrieve information about a string. Other commands
manipulate the string and return the results. For example, the length()
function returns the number of characters in a string. The substring()
function returns the specified portion of a string. Refer to the sanScript
Reference for a complete list of commands that work with strings.
Comparing strings
When strings are compared in boolean expressions, the ASCII values of the
characters in the strings are compared, one character at a time from left to
right, until a character doesn’t match or one of the strings has no more
characters.
ASCII values for lowercase characters are larger than ASCII values for uppercase
characters.
58 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A PT E R 6 P ER FO R M I N G C AL C U L A TI O N S
• Decision structures
• Loop structures
Decision structures
Two decision structures are available for use in scripts.
If then...end if
The if then...end if statement is one of the structures you can use to make
decisions. The if then...end if statement evaluates boolean expressions and,
based on the results, executes a set of sanScript statements.
If the purchase amount is greater than the credit limit, an error message is
displayed and the Transaction Complete field is set to false. Otherwise, the
Transaction Complete field is set to true.
Case...end case
The other decision structure you can use in scripts is the case...end case
statement. Like the if then...end if statement, this statement allows a series
of statements to run on a conditional basis.
The following script uses the case...end case statement to o set the rebate
amount to be paid to customers based upon their total purchases for the
fiscal year.
Loop structures
In cases where you want a set of sanScript statements to be executed
repeatedly, you will use a loop structure. Several loop structures are
available in sanScript.
For loop
The for loop iterates a variable through a specified range of variables. They
are useful when you need to use the value of the variable being iterated,
such as for an array index.
The following script uses a for do...end for statement to set the fields of the
Monthly Total array to 0.
local integer i;
for i = 1 to 12 do
'Monthly Total'[i] = 0;
end for;
62 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 7 P R O G R AM S T R U C T U R E S
Repeat loop
The repeat loop runs a set of sanScript statements, then tests a boolean
condition to determine whether the loop should be repeated. Using a
repeat...until statement ensures that the statements within the loop will be
run at least once.
The following script uses a repeat loop to read through all of the records in
a temporary table.
While loop
The while loop tests a boolean expression. If the expression is true, the set
of sanScript statements in the loop is run. Using a while do...end while
statement ensures that the condition of the loop is tested before any of the
statements within the loop are ever executed.
The following example uses a while loop to read through all of the records
in a table.
• Chapter 12, “Tree View,” explains how to use the tree view control.
• Chapter 13, “List View,” explains how to use the list view control.
66 P R O G R A M M E R ’ S G U I D E V O L U M E 2
Chapter 8: Standard Controls
Dexterity provides several standard controls, such as push buttons, check
boxes, and radio buttons. This portion of the documentation provides
detailed information about how to use these controls in your application.
Information about the standard controls is divided into the following
sections:
A button drop list can have up to three images: an Up image, a Down image
and an Over image. The Up image is displayed when the button isn’t being
clicked. The Down image is displayed when the button is being clicked. The
Over image is displayed when the pointer is positioned over the button
drop list.
You can use the Field_SetImage() function to specify the images to use for a
button drop list from within scripts.
Use the Style property for the button drop list field to specify which types
of items are displayed on the button. The Style property also controls the
arrangement of the items.
If you select 3D Highlight for the Appearance property, the text caption in the
button portion of the button drop list will become bold when the field has the focus.
Be sure to provide extra space for the caption to accommodate the bold text.
Drop indicator
Typically, button drop lists have a drop indicator (a small triangle) in the
lower-right corner of the field. It’s not necessary to include the drop
indicator in the image for the button drop list. Instead, set the
DropIndicator property to True. Use the DropPosX and DropPosY
properties to specify the distance the drop indicator will appear from the
lower-right corner of the button drop list.
Cascading items
In some cases, you may want to have a hierarchy in the items appearing in
the list. To do this, you must build the contents of the button drop list with
sanScript code. The Field_BDLCreateSubmenu() and
Field_BDLAddToSubmenu() functions can be used to create cascading or
“pull-right” items in button drop lists. The following illustration shows a
button drop list with cascading items.
68 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 8 S TA N D A R D C O N T R O L S
Check boxes
You can use picture static values with check boxes. In addition, you can use
the Style property to specify whether the check box will appear as a
standard check box or as a push button. The following illustrations show
how check boxes look with the various styles.
Not marked
If you change the Style property for a check box that uses static picture values, use
the Size to Default tool in the toolbox to resize the check box field to the appropriate
size.
If you use pictures (picture resources) as the static values for a push button-
style check box, you can specify two, four or six images. These images are
used for the various check box states.
• If you specify two images, these images will be used for the up and
down states, respectively.
• If you specify four images, these will be used for the up, up clicked,
down, and down clicked states, respectively.
• If you specify six images, the Appearance property for the check box
must be set to 3D Highlight. The images will be used for the up, up
clicked, down, down clicked, up highlight and down highlight states,
respectively.
The following illustration shows the six picture static values defined for the
Note check box field.
Push buttons
Push buttons provide a method of starting processing in an application.
Use the Style property for the push button field to specify which types of
items are displayed on the button. The Style property also controls the
arrangement of the items.
70 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 8 S TA N D A R D C O N T R O L S
The following table shows the arrangements possible for text and graphics
on a push button.
Style Example
Text Only
Graphic Only
Text on Top
Text on Bottom
Text on Right
Text on Left
If you supply only the Up image for a push button, the Down image will be
drawn automatically when the push button is clicked.
You can use the Field_SetImage() function to specify the images to use for a push
button from within scripts.
Appearance
The Appearance property specifies what type of border appears for a push
button. A push button can have a 2-D border or 3-D border.
When designing the button graphics, keep in mind that the 3D border will
be drawn on top of the outer two rows of pixels on the button up and
button down images.
You can also use the Appearance property to have push button display with
a 3-D highlight. With this setting, the push button has a flat appearance
until the mouse pointer is moved over the button. Then the button displays
a 3-D appearance. An example is shown in the following illustration.
If you select 3D Highlight for the Appearance property, the text caption in the push
button will become bold when the field has the focus. Be sure to provide extra space
for the caption to accommodate the bold text.
Behavior
Several properties for push buttons control their behavior.
Default
Use this property to specify which push button field change script will run
when the user presses ENTER when the window is active. Only one push
button in a window should have this property set to true. Typically, this
property is set to true for an OK button that appears in the window.
72 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 8 S TA N D A R D C O N T R O L S
Cancel
Use this property to specify which push button field change script will run
when the user presses ESC when the window is active. Only one push
button in a window should have this property set to true. Typically, this
property is set to true for a Cancel button that appears in the window.
Hyperspace
This property should be set to true for Close or Cancel buttons appearing in
the window. When Hyperspace is set to true, the Close or Cancel button can
be clicked without running the change script or post script for the currently-
focused field. Only the change script for the Close or Cancel button will
run. This allows the user to close the window, even though the change or
post script for the current field may have prevented it.
Radio buttons
To make radio buttons work properly in your application, the radio group
and radio button fields must be arranged properly in the window. In the tab
sequence, the radio group must come immediately before the radio buttons.
For example, in the Payment Method radio group shown in the following
illustration, the Payment Method radio group field comes immediately
before the Cash, Check and Charge radio button fields in the tab sequence.
String fields
You can use the AutoComplete property for string fields to automatically
fill the contents of the field as the user types, based on the values previously
entered for the field.
Using auto-complete
To use the auto-complete capability for a string field, you must set the
AutoComplete property to true for every occurrence of the field for which
you want the functionality. When a user adds a value to the field and moves
the focus, the value is added to the auto-complete list for the field.
When the user begins to type characters into a field with the AutoComplete
property set to true, the auto-complete list for the field will be examined to
find any matches. The matching items will be displayed in a drop-down
list. The user can continue typing a value to refine the items listed, or use
the arrow keys or mouse to select one of the items in the list. If drop-down
list isn’t large enough to fully display the auto-complete items, use the
resize area in the corner of the list to expand the list.
To remove an item from the auto-complete list, right-click the item and
choose Remove From List in the menu that appears.
74 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 8 S TA N D A R D C O N T R O L S
If you’re running your application in test mode, auto-complete items will work for
the current session, but are not stored in this file.
76 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 8 S TA N D A R D C O N T R O L S
Text fields
Use the EditMode, ScrollBars and WordWrap properties to specify the
characteristics of text fields on the window:
• By default, text fields display scroll bars. To hide the scroll bars, set the
ScrollBars property to false.
• The WordWrap property controls whether text wraps in the text field.
By default, text does not wrap in a text field. Set the WordWrap prop-
erty to true to make text wrap.
Controls on toolbars
Toolbars typically contain a row of push buttons or button drop lists that
allow the user to navigate to other windows in the application. Toolbars
should contain only fields that don’t need to keep the focus to operate: push
buttons, drop-down lists, button drop list or visual switches. Toolbars can
also contain fields for which the Editable property is set to false.
Tooltips
Most controls allow you to specify a tooltip, which is a small piece of text
that describes the control. The tooltip is displayed when the user moves the
pointer over the top of a control, as shown in the following illustration.
Often, tooltips are used for fields that require additional explanation, such
as buttons on a toolbar. To add a tooltip, select the control in the layout
window and enter a value for the Tooltip property. You can also use
the Field_SetToolTip() function to set a tooltip from a script. Use the
Field_GetToolTip() function to retrieve a tooltip.
Composites should be composed of only string and numeric fields, and the
total length of the composite must not exceed 128 characters. While it is
possible to include some other control types in composites, we don’t
recommend it. Text and picture control types should never be included as
composite components.
80 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 9 C O M P O S IT E S
Example 1
This example shows a composite item number with two components.
GPS-1151
The first part is a vendor code, which is alphabetic, while the second part is
the actual part number, which is numeric, so a composite consisting of a
string component and an integer component was used for this field. The
string component was formatted as alphanumeric, while the integer
component allows only numbers to be entered. The format string 1111-2222
was applied to the composite to add the dash between the two components.
82 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 9 C O M P O S IT E S
When you add the extended composite field to a window, you must set the size of
the field in the window manually, since Dexterity has no way of knowing how large
to make the field.
Example 2
This example describes how to create and use an extended composite for a
customer number with up to 12 components. Twelve string data types and
fields were created for the extended composite, one for each component.
The following “starting” format string was used when the format for the
composite was created:
11111-22222-33333
The starting format string serves only as a placeholder. The structure and
number of components aren’t critical because the format will be overridden
at runtime.
A field was created using the composite data type, placed on the main
window of the Main Menu form and made invisible and display only by
setting the Visible and Editable properties for the field to false.
The following is the Main Menu form pre script used to specify the
characteristics of the customer number composite:
local integer segment;
local string separator, width_char;
local integer maximum_num_segments, num_segments;
local integer seg_length[12];
local boolean scroll_arrows;
maximum_num_segments = 12;
{Specify how long each segment will be when it's stored and displayed.
In an actual application, this information would be stored in a
table.}
seg_length[1] = 5;
seg_length[2] = 4;
seg_length[3] = 4;
seg_length[4] = 2;
seg_length[5] = 2;
seg_length[6] = 2;
seg_length[7] = 2;
seg_length[8] = 2;
seg_length[9] = 2;
seg_length[10] = 2;
seg_length[11] = 2;
seg_length[12] = 2;
84 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 9 C O M P O S IT E S
{Use the override component statement to set the storage and display
length of each component of the composite. If a segment at the end of
the composite won't be used, set its length to 0.}
Example 3
The following script assigns the values shown in Example 1 to the Item
Number composite field.
Example 4
The following example sets the local variable l_company to the value of the
second component of the Part Number composite field.
Example 5
The following example clears all 12 components of the Account Number
field.
local integer index;
Component-based scripts
Like other fields, composites can have pre, change and post scripts for the
entire field. In addition, the pre, change and post field scripts will be run for
each component if the Call Component Scripts option is marked in the Data
Type Definition window for the composite.
Note there is only one pre, change and post script for the composite field,
not a pre, change and post script for each component of the field. If
component scripts are enabled, the pre, change and post scripts for the
composite field will be run once for each component of the composite and
once for the composite as whole. The currentcomponent() function is used
to determine which component the focus is in when the pre, change or post
script is run.
86 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 9 C O M P O S IT E S
The following table shows which scripts run and the value returned by the
currentcomponent() function when a user moves into, through, and out of
a three-component composite field.
Example 7
Component scripts for composites are often used to validate components of
the composite as they are entered by the user. The following example is a
change script for the Part Number composite field for which component
scripts are called.
The first component of the Part Number field is the Vendor Number. The
change script validates the Vendor Number component by verifying that
the vendor exists in the Vendors table. If the vendor doesn’t exist, a warning
will be displayed and the field will be restarted. If the vendor does exist, the
remainder of the part number will be entered and the change script will
attempt to retrieve the entry.
if (currentcomponent() = 0) then
{Change script is run for the entire Part Number field.}
{Retrieve the information about the part.}
'Part Number' of table Current_Inventory = 'Part Number' of
➥ window Inventory_Maintenance;
change table Current_Inventory;
if err() = OKAY then
copy from table Current_Inventory;
end if.
else
if (currentcomponent() = 1) then
{Validate the Vendor component.}
'Vendor Number' of table Vendors = 'Part Number:Vendor Number';
get table Vendors;
if err() <> OKAY then
warning "This vendor hasn't been entered. Please select
➥ another.";
restart field;
end if;
end if;
end if;
Comparing composites
When composites are compared in boolean expressions, the corresponding
components of the composite are compared. For the composite comparison
to be true, the comparison between the corresponding components must be
true for all components of the composites. Otherwise, the comparison will
return a value of false.
For example, these two composites shown in the following illustration are
compared to determine whether they are equal.
Related items
Use the information listed to learn more about creating and using
composites.
Commands
currentcomponent(), override component, override field
Additional information
Chapter 6, “Data Types,” Chapter 7, “Formats,” and Chapter 8, “Global Fields” in Volume
1 of the Dexterity Programmer’s Guide
88 P R O G R A M M E R ’ S G U I D E V O L U M E 2
Chapter 10: Scrolling Windows
Scrolling windows display records from a table, allowing a user to scroll
through records at runtime. Scrolling windows are useful when the number
of items to display is unknown, such as items in an inventory list. Scrolling
windows are also used for data entry, such as transactions made up of
multiple entries.
Scrolling windows are unlike standard windows in that they never appear
by themselves in a Dexterity application. Like fields, they must be added to
the layout area of an existing window. A scrolling window is linked to a
table and operates by displaying one line of information in the scrolling
window for each record in the table. You can display any field from a record
in the table, but usually only the most important fields from each record are
displayed in the scrolling window.
A browse-only scrolling
window allow you to
select only one item in the
scrolling window.
90 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A PT E R 1 0 S C R O L L I N G W I N D O W S
An adds-allowed scrolling
window has a blank line that
allows you to add items to
the scrolling window.
After you’ve drawn the scrolling window on the layout window, the
Scrolling Window Options window will appear, as shown in the following
illustration. You will use this window to begin specifying characteristics of
the scrolling window.
Name
Only one name is required for the scrolling window, the name used in
scripts to refer to the window. To make the name easier to use in scripts,
you can use underscores (_) between parts of the name instead of spaces.
Linked table
Every scrolling window must be linked to the table that contains the fields
you will add to the scrolling window. The Link Table list in the Scrolling
Window Options window allows you to select one of the tables you created
for your application. This table also must be attached to the form where the
scrolling window is located. Unless specified otherwise in a script, sorting
for the items in the scrolling window will be determined by the key you
select in the Link Key list.
92 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A PT E R 1 0 S C R O L L I N G W I N D O W S
You can resize the scrolling window by selecting it and dragging one of its
resize handles. You can select the scrolling window and then use the
Properties window to change the visual characteristics of the scrolling
window.
Once the scrolling window is drawn, you can begin the process of creating
the scrolling window layout. Like all Dexterity windows, scrolling
windows use a layout area so you can add fields and other objects to the
window. To display the layout area for a scrolling window, double-click the
scrolling window in the window layout.
When you create the layout for a scrolling window, you are creating the
layout for only one line of the scrolling window. This line is drawn in
the scrolling window once for each record in the linked table, until the
scrolling window is full. The fields you add to the scrolling window
determine what each line in the scrolling window will display at runtime.
The area in the scrolling window layout that corresponds to one line in the
scrolling window is the area between the dashed line and the top of the
layout area. This is known as the small line item. To mark the small line item,
choose Mark Small Line Item from the Tools menu and click in the window
where you want the dashed line to appear. Fields you drag into the
scrolling window layout should be placed between the top of the layout
window and the dashed line. In most cases it’s easier to drag the fields to
the window first, then mark the small line item.
As you add fields, you can use the same visual and object properties used
when you design other windows. When you’ve finished designing the
scrolling window layout, close the layout window to redisplay the layout
for the window containing the scrolling window.
Property Description
AltLineColor If this property is set to true, alternate lines of the scrolling
window will appear with a different color. Use the
Field_SetAltLineColor() function to specify the alternate line
color.
Resize-Horizontal Specifies the horizontal resize behavior for the scrolling window
when per field resizing is used. For more information, refer to
Resizing windows on page 146 of Volume 1 of the Dexterity
Programmer’s Guide.
Resize-Vertical Specifies the vertical resize behavior for the scrolling window
when per field resizing is used. For more information, refer to
Resizing windows on page 146 of Volume 1 of the Dexterity
Programmer’s Guide.
Property Description
DefaultDblClick In a browse-only scrolling window, setting the DefaultDblClick
property to true provides a short cut, allowing the user to double-
click a line in the scrolling window to accomplish the action of
selecting a line and clicking the push button whose Default
property set to true.
HelpContextID This is the help context ID for the scrolling window.
94 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A PT E R 1 0 S C R O L L I N G W I N D O W S
Property Description
IgnoreActiveLine This property controls focus and fill behavior of the scrolling
window.
When this property is set to true, Dexterity will use the traditional
behavior for scrolling windows. When focus moves to the
scrolling window through the tab sequence, the focus will move to
the first line. When the focus statement is used, the focus will
move to the active line if visible, or the first line if it is not.
When the scrolling window is filled from the current record,
Dexterity will position the active line as the first line in the
scrolling window.
LinkTable This is the table linked to the scrolling window. Records displayed
in the scrolling window come from this table. This is originally set
in the Scrolling Window Options window.
LinkTableKey This is the key for the linked table that specifies the default sorting
order for the records in the scrolling window. This is originally set
in the Scrolling Window Options window.
Name This is the name of the scrolling window. This is originally set in
the Scrolling Window Options window.
ScrollToBottom Setting this option to true causes the scrolling window to display
records at the end of the linked table, rather than at the beginning.
In effect, the scrolling window has been scrolled to the bottom of
the table. This is useful for adds-allowed scrolling windows
because the user doesn’t have to scroll to the end of the window
to add a new item.
WindowID This is the resource ID of the scrolling window.
WindowType This property specifies whether you want a browse-only,
editable or adds-allowed scrolling window.
An adds-allowed scrolling window has a blank line at the bottom
of the window where the user can add new lines to the window
and save them in the linked table.
A browse-only scrolling window allows the user to select only one
line in the scrolling window and make no changes.
An editable scrolling window allows the user to edit and change
the contents of lines in the scrolling window. These changes can
be saved in the linked table.
The following illustrations show the layout for the Customer_List scrolling
window. In normal mode, the window will display the Customer Number
and Customer Name. In expanded mode, it will display the Customer
Number and Name, as well as the Address, City, State, and ZIP Code fields.
The expand window statement is used to switch between normal mode
and expanded mode.
96 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A PT E R 1 0 S C R O L L I N G W I N D O W S
Window scripts
Scripts attached to the window containing the scrolling window and scripts
for fields in the same window are used to fill the scrolling window and
control how it displays information.
For more information Typically, the window pre script for the window containing the scrolling
about the fill window window contains the fill window statement that fills the scrolling window
statement, refer to the from the linked table using the table key specified. If no table key is
SanScript Reference specified, the key selected in the Scrolling Window Options window is
manual. used. The fill window statement can also fill a scrolling window from a
different table.
The table keys for the table linked to the scrolling window control how the
items in the scrolling window are sorted. A drop-down list or other list field
can be used to specify which table key will be used to sort the records when
displaying information.
Example 1
The following example shows the window pre script for the window
containing the Customer_List_Scroll scrolling window. The pre script sets
the Sort By drop-down list to 1, indicating the first key will be used to sort
the items in the scrolling window. The fill window statement fills the
scrolling window from the linked table using the first key for the table.
98 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A PT E R 1 0 S C R O L L I N G W I N D O W S
Example 2
The following example is the change script for the Sort By drop-down list,
which controls how the records in the scrolling window are sorted. It uses
the fill window statement to fill the scrolling window using the
appropriate table key to sort the records.
Example 3
The following window pre script and change scripts for the normal and
expand buttons control whether the scrolling window displays information
in normal mode or expanded mode. The normal and expand buttons are
visual switches, each with two states indicating the two different modes.
To access the scripts for the scrolling window, open the window containing
the scrolling window. Double-click the scrolling window to open the
scrolling window’s layout. Select the scrolling window, and then click the
Script tab in the Properties window. Select the type of script you want to
add or edit, and click the lookup button.
100 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A PT E R 1 0 S C R O L L I N G W I N D O W S
The line pre script should not be used to selectively show/hide, lock/unlock or
enable/disable fields for individual lines in the scrolling window. If you do so, the
scrolling window will not redraw properly.
The reject record statement can be used in the line fill script to keep
specified records from being displayed in the scrolling window. You should
avoid rejecting a large number of records using the line fill script because
this can reduce application performance considerably.
Fill scripts are also useful for reading information from tables other than the
scrolling window’s link table. The following script uses the Part Number
field from the Inventory_Data file to read a description from the Item_List
file.
'Part Number' of table Item_List = 'Part Number' of table
➥ Inventory_Data;
{Read from the additional table.}
get table Item_List;
if err() = OKAY then
{Copy the information to the scrolling window.}
Description of window Inventory_Scroll = Description of table
➥ Item_List;
end if;
102 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A PT E R 1 0 S C R O L L I N G W I N D O W S
The line insert script should be used only for scrolling windows that have
the order of the items controlled by sequence numbers. For example, the line
items for an invoice entry window typically use sequence numbers.
Sequence numbers are currency values that are stored as part of the table
and are also part of the table key. They act as reference values when the
scrolling window displays information, ensuring that the records are
displayed in the proper order.
The line items are stored in a table that has the Invoice Number and
Sequence Number as keys. Note that the sequence number for each line
item increased by 512. This was done by the line change script in the
scrolling window when the line items were saved. Having the sequence
numbers in the table key ensures that the invoice items will always be
displayed in the order they were entered.
The following script is the line insert script for the scrolling window
displaying the invoice line items. It calculates a new sequence number
based upon where the focus is currently located in the scrolling window
and saves a new record in the table, allowing the user to add a line item.
The scrolling window will be redrawn automatically.
If this focus were on the last item in the scrolling window, Steel Wool, and
the user chose to insert a row, a new record would be created in the
Invoice_Data table with the Invoice Number 4510 and the Sequence
Number 768. This ensures that the new item will appear in the scrolling
window before Steel Wool and after the Torx Screwdriver.
104 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A PT E R 1 0 S C R O L L I N G W I N D O W S
Using lookups
Lookups can be used for fields in the scrolling window. One method of
implementing a lookup is to specify a lookup form for a global field that is
used in the scrolling window. Use the Field Definition window to specify a
lookup form for the global field. When the focus is in that field in the
scrolling window and the user chooses Lookup, the designated lookup
form will be opened and a value returned to the scrolling window field.
You can also use directed hyperspace mode and link a scrolling window
field to a push button in the window where the scrolling window is located.
To do this, you must instead link the scrolling window field to another
push button you’ve added to the scrolling window layout. This additional
push button should be made invisible and non-editable and should be
located below the big line item so it isn’t displayed in the scrolling window.
This “intermediate” push button runs the script for the lookup button
located in the window. This is shown in the following illustration.
Run script
Single-line scrolling windows are used for data entry when many fields are
required for each record. The single-line layout allows a logical
organization of fields and provides more area for prompts.
106 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A PT E R 1 0 S C R O L L I N G W I N D O W S
When a scrolling window displays only a single line, the scroll bar is
automatically hidden. Push buttons in the scrolling window are typically
used to move to the next or previous records. The change scripts attached to
these push buttons use the Window_ScrollScrollingWindow() function to
perform the scrolling.
Select a table key in the Link Key list. This key determines the order in
which records will be sorted if another key isn’t specified when the
scrolling window is filled. Click OK to close the Scrolling Window
Options window.
Property Description
AltLineColor If this property is set to true, alternating lines of the scrolling
window will appear with a different color. Use the
Field_SetAltLineColor() function to specify the alternate line
color.
Property Description
DefaultDblClick In a browse-only scrolling window, setting the
DefaultDblClick property to true provides a short cut, allowing
the user to double-click a line in the scrolling window to
accomplish the action of selecting a line and clicking the push
button that has the Default property set to true.
108 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A PT E R 1 0 S C R O L L I N G W I N D O W S
Property Description
HelpContextID This is the help context ID for the scrolling window.
LinkTable This is the table linked to the scrolling window. Records
displayed in the scrolling window come from this table.
LinkTableKey This is the key for the linked table that specifies the default
sorting order for the records in the scrolling window.
Name This is the name of the scrolling window.
ScrollToBottom Setting this option to true causes the scrolling window to
display records at the end of the linked table, rather than at
the beginning. In effect, the scrolling window has been
scrolled to the bottom of the table. This is useful for adds-
allowed scrolling windows because the user doesn’t have to
scroll to the end of the window to add a new item.
WindowID This is the resource ID of the scrolling window.
WindowType Specify whether you want a browse-only, editable or adds-
allowed scrolling window.
If you need to resize the small line item or big line item, just choose Mark
Small Line Item or Mark Big Line Item from the Tools menu and mark the line
again.
A window pre script for the window containing the scrolling window
usually includes the fill window statement, used to fill the scrolling
window with data from the scrolling window’s linked table. The
following table shows the other scripts that can be attached to the
scrolling window.
Related items
Use the information listed to learn more about creating scrolling windows.
Commands
delete line, expand window, fill window, insert line
Additional information
Chapter 11, “Lookups.” in Volume 2 of the Dexterity Programmer’s Guide
110 P R O G R A M M E R ’ S G U I D E V O L U M E 2
Chapter 11: Lookups
Lookup windows allow a user to easily enter key information in an editable
field. One method of opening a lookup window is to use the Lookup menu
item, typically added to the Help menu. Another method is to use a push
button in the window with a script attached to open the lookup window.
For example, the following illustration shows the Sellers window for the
Real Estate Sales Manager application. Values for the Seller ID field can be
returned by the lookup window for that field.
112 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 1 1 L O O K U P S
Implementing lookups
Two methods are used to implement lookups. The first method involves
specifying the lookup form for a global or local field definition. The second
method involves creating a lookup push button and setting up a lookup
link in the layout window.
When the focus is in the Customer ID field and the Lookup menu item is
chosen, the Customer_Lookup form will open. A return path will be set up
back to the Customer ID field. The Customer_Lookup form will return the
selected value to the Customer ID field.
If a lookup form has been designated for a field, the lookup window will
work for any occurrence of the field in the application. For instance, in the
previous example, the lookup would work anywhere you used the
Customer ID field in the application. If you don’t want a specific instance of
the field to access the lookup form, set the DisableLookup property for that
instance of the field to true.
The lookup button for the Buyer ID field opens the Buyer_Lookup form
and sets up the return path back to the Buyer ID field. The lookup form uses
the return statement to return a value to the lookup field. The following
script is attached to the lookup button.
One way to open the lookup is to click the lookup button. Dexterity also has
the capability to make the Lookup menu item open the lookup form by
effectively “clicking” the lookup button. To do this, open the layout
window containing the field and lookup button. Choose Link Lookup from
the Tools menu; Dexterity will enter lookup linking mode. Simply click
on the field and drag to the lookup button. A flashing black line indicates
the link was made.
Choose Link Lookup from the Tools menu to exit lookup linking mode.
114 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 1 1 L O O K U P S
In the preceding example, whenever the focus is in the Buyer ID field, the
Lookup menu item will be enabled. Choosing the Lookup menu item will
run the change script for the lookup button, which in turn opens the lookup
form and sets up the return path.
You should also set the Hyperspace property for the lookup button to True.
This will prevent the change script or post script for the editable field from
running. The change script or post script could potentially keep the lookup
button script from running.
Information about tree view fields is divided into the following sections:
• Terminology
• Tree view nodes
• Tree view properties
• Tree view scripts
• Adding nodes to tree view fields
• Image types for tree view nodes
• Adding images to a tree view field
• Image sizes
• Specifying the images to use
• Background colors
• Searching tree view fields
Terminology
When implementing tree view fields, it is helpful to understand the
terminology used to describe them. A tree is composed of nodes. A node
represents one piece of information in the tree.
Nodes are often classified based on their relationship to other nodes. The
following illustration shows a parent node and child nodes.
A group of child nodes that have the same parent are referred to as siblings.
118 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A PT E R 1 2 T R E E V I E W
Root-level nodes are those that appear in the first level of the tree view.
Node ID The node ID is a long integer that uniquely identifies each node.
You will use the node ID whenever you reference a node from within
sanScript.
Label The label is the text that appears for the node.
Data item Each node has a long integer value associated with it that you
can use to store data specific to a node.
Images Each node can have several images associated with it. One image
is displayed when the node is selected. Another is displayed when the node
is not selected. Additional images can be used to “overlay” the node image.
Still other images can be used to show the state of a node. Use of images is
discussed in the section titled Image types for tree view nodes on page 124.
Select script
The select script runs each time a different node is selected in the tree view
field.
Post script
The post script runs when the focus leaves the tree view field.
120 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A PT E R 1 2 T R E E V I E W
Expand script
Refer to Adding nodes The expand script runs each time a parent node is expanded in the tree
to tree view fields on view field. Use this script to add child nodes when a parent node is
page 123 for more expanded. Use the TreeView_GetExpandingNode() function to retrieve
information about how the ID of the node being expanded.
to add nodes to tree
view fields. The following example is the expand script for the Address List tree view
field. It adds child nodes containing customer information to the node
being expanded.
Collapse script
The collapse script runs each time a parent node is collapsed in the tree
view field. Use this script to remove child nodes from the node being
collapsed. Removing the non-visible nodes allows the tree view field to
operate more efficiently and be more responsive. Use the
TreeView_GetCollapsingNode() function to retrieve the ID of the node
being collapsed.
The following example is the collapse script for the Address List tree view
field. It removes the child nodes from the node being collapsed.
The following example is the state click script for the Address List tree view
field. It toggles the state image displayed when the state image is clicked.
local long click_node;
local integer previous_image;
122 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A PT E R 1 2 T R E E V I E W
Double-click script
The double-click script runs each time a node is double-clicked. If a node
contains children, the node will be expanded or collapsed and the expand
or collapse script will run. Then the double-click script will run. Use the
TreeView_GetSelectedNode() function to retrieve the node ID of the node
that was double-clicked.
• If the data used for the tree view field changes often, only the most
recent data is used when new nodes are added and displayed.
• The performance of the tree view field will scale as more nodes are
added. This means that performance will be optimal, even as more
nodes are added to the tree view field.
Item images
The item image is displayed with the node.
State images
The state image is displayed next to the node image. In the following
illustration, the check box next to each node is a state image.
The X is a state
image.
Overlay images
Overlay images allow you to add to the node image without requiring
several versions of the icon. For example, an overlay image containing a
slash could be placed on top of a folder icon, indicating the item can’t be
accessed.
The
The overlay
overlay image
image isis
placed
placed on
on top
top of
of the
the
item image.
124 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A PT E R 1 2 T R E E V I E W
Item images
Click the Item Images tab to specify item and overlay images that will be
used for nodes in the tree view field. To add a new image, click New.
Only four different images can be used as overlay images at one time.
State images
Click the State Images tab to specify the state images that will be used for
the tree view field. To add a new image, click New.
Image sizes
When a tree view is drawn, it must provide enough room for any images
associated for each node. The size of the first item image added to the tree
view data type is used when drawing the tree. For example, if the first item
image for the tree field is 20 by 20 pixels, an area that size will be used for
every node. Images of other sizes are scaled to fit in the area provided. For
the best appearance, we recommend that all images in the tree view be the
same size.
State images are always 16 by 16 pixels. If you will be using state images in
your tree view field, we recommend that all of your images be 16 by 16
pixels.
Background colors
Typically, you will want the images used in a tree view field to blend with
the background color of the field. The color of the image’s first pixel (upper
left corner) specifies which color will be considered transparent, allowing
the field’s background color to show through any pixel of that color.
126 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A PT E R 1 2 T R E E V I E W
When you create the images for the list view, you may want to use a unique color
for the image background. That way, the list view background color will not
interfere with the other colors in the image.
This is a depth-first search, meaning that child nodes are searched before
sibling nodes. To make this a breadth-first search, in which sibling nodes
are searched before child nodes, simply swap the two shaded sanScript
statements in the script above.
This is a depth-first search, meaning that child nodes are searched before
sibling nodes. To make this a breadth-first search, in which sibling nodes
are searched before child nodes, simply swap the two sanScript statements
indicated in the above illustration.
128 P R O G R A M M E R ’ S G U I D E V O L U M E 2
Chapter 13: List View
A list view displays data in icon form or as a multicolumn list. A typical list
view field is shown in the following illustration.
Information about the list view is divided into the following sections:
• Terminology
• List view items
• List view properties
• List view scripts
• Image types for list views
• Adding images to a list view field
• Image sizes
• Specifying the images to use
• Background colors
• Working with list views
• Working with the selection
• Searching a list view
Terminology
When implementing list view fields, it is helpful to understand the
terminology used to describe them. A list is composed of items. An item
represents one piece of information in a list.
List view fields can display items in four ways. In large icon view each item is
displayed with the large version of its associated icon.
In small icon view, each item is displayed with the small version of its
associated icon.
130 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 1 3 L I S T V I E W
In list view, items are displayed as a list, along with the small version of their
icons.
If properties for the list view field are set appropriately, users can sort
columns by clicking the column headings and rearrange columns by
dragging them. They can also resize columns.
Index The index is a long integer that specifies the position of the item in
the list. You will use the index whenever you reference a list item from
sanScript.
Be aware that the index value of a list item may change. For example, when the list
is re-sorted, an item may appear in a different location in the list. Thus, its index
value will have changed.
Label The label is the text that appears with the item.
Data item Each item has a long integer value associated with it that you
can use to store data specific to the item.
Images Each item can have several images associated with it. One image
is used as the icon displayed with the item. Additional images can be used
to “overlay” the icon image. Still other images can be used to show the state
of an item. Use of images in list views is discussed in detail in Image types for
list views on page 136.
Subitems Each list view item can have several subitems associated with
it. A subitem typically consists of a single string value. Subitems can be
displayed when the list view is shown in report view mode. They are not
shown in the other view modes. Each subitem can also have an additional
currency, date, time or integer component that stores a value to be used for
sorting the contents of the list.
By default, a list view will use the string component of a subitem when
sorting a column in the list view. The list view items will be sorted in ASCII
order, which may not be appropriate if the column contains currency,
integer, date or time values. If the subitems in the column have an
additional currency, integer, date or time component, these additional
components will be used to sort the list.
132 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 1 3 L I S T V I E W
Post script
The post script runs when the focus leaves the list view field.
The following example is the item changed script for the Explorer List list
view field. It updates the Object Count field with the number of items
selected in the list view.
local long selection_count;
134 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 1 3 L I S T V I E W
Double-click script
The double-click script runs each time an item is double-clicked. Double-
clicking an item will change the selection, so the selection changed script
will run prior to the double-click script. Use the ListView_GetClickItem()
function to retrieve the index of the item that was double-clicked.
The following example is the state click script for the Explorer List list view
field. It toggles the state image displayed when the state image is clicked.
local long click_item;
local integer previous_image;
Icon images
The icon image is displayed with the item.
State images
In report view mode, the state image is displayed next to the icon image. In
the following illustration, the check box next to each item is a state image.
Overlay images
Overlay images allow you to add to the icon image without requiring
several versions of the icon. For example, an overlay image containing the
word “SOLD” could be placed on top of the house icon, indicating the
house has been sold.
136 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 1 3 L I S T V I E W
Item images
Click the Item Images tab to specify icon images and overlay images that
will be used for the list view field. To add a new image, click New. Each
image can have a large version and a small version. The large image is used
for the large icon view mode. The small image is used for all other view
modes.
State images
Click the State Images tab to specify the state images that will be used for
the list view field. To add a new image, click New. Because state images are
shown only in report view mode, there is only a small version of the image.
Image sizes
When the list view is drawn, it must provide enough room for the images
associated with each item. The size of the images used is specified by the
ImageSize property of the list view field. If this property is set to System,
large images are scaled to 32 by 32 pixels. Small images are scaled to 16 by
16 pixels. If the ImageSize property is set to First Image, all large images are
scaled to the size of the first large image you define for the list view field.
Likewise, all small images are scaled to the size of the first small image.
If you use the System setting for the ImageSize property, we recommend
that your large images be 32 by 32 pixels. The small images should 16 by 16
pixels. Your images will then have the best appearance because they won’t
have to be scaled. If you allow the image sizes to be based on the first
image, we recommend that all the large images be a uniform size. All small
images should also be a uniform size.
If you don’t supply both the small and large images, the image supplied will scaled
to the proper size and used in place of the missing image.
Background colors
Typically, you will want the images used in a list view field to blend with
the background color of the field. The color of the image’s first pixel (upper
left corner) specifies which color that will be considered transparent,
allowing the field’s background color to show through any pixel of that
color.
138 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 1 3 L I S T V I E W
When you create the images for the list view, you may want to use a unique color
for the image background. That way, the list view background color will not
interfere with the other colors in the image.
• Finally, if the list view can be displayed in report view mode, use the
ListView_ColumnAdd() function to add columns to the list. If you
don’t add columns, no items will be shown in report view mode.
If the list view field is shown in report view mode, add all of the items and subitems
before adding columns the list. This prevents the list view field from flashing as
items are added, because the items are shown only after columns are added.
Removing all items from a list view field does not affect the columns
t h a t h a v e b e e n a d d e d . To r e m o v e a l l c o l u m n s , u s e t h e
ListView_ColumnRemove() function, and use the constant LV_ALL to
indicate all columns should be removed.
If your application will displaying an empty list view field, consider using the
ListView_SetEmptyMessage() function to specify a message to be displayed in
the empty list view field.
Sorting
The SortMethod property specifies the default sorting order for items in the
list view field. You can use the ListView_Sort() function to re-sort the items
in the list using another sorting method. When you add items to the list
view field, they will always be added to the end of the list, regardless of the
current sorting order. Use the ListView_Sort() function to re-sort the list
after adding items.
By default, the list view will use the string component of a subitem when
sorting a column in the list view. The list view items will be sorted in ASCII
order, which may not be appropriate if the list view field contains non-
string values, such as currency, integer, date or time values. If this is the
case, you can use functions such as ListView_ItemSetIntSubitem() and
ListView_ItemSetCurrencySubitem() and to add an additional integer,
currency, date or time component to each subitem. If the subitems for a
column contain an additional component, its value will be used as the basis
for sorting the column.
If all the subitems for a column don’t contain the same type of additional
component, the items in that column may not sort consistently. To ensure
consistent sorting, be sure all subitems in a column have the same type of
additional component.
Sizing columns
When you add a column to a list, it will be wide enough to fully display the
column heading. If items in the column are wider than the heading, they
will be truncated. You can use the optional width parameter to specify the
width of the column when you add it. The ListView_GetStringWidth()
function is useful when specifying the width of a column. You pass a string
to this function to find out how wide the column must be to contain the
string without truncating.
If the column has already been added to the list, you can use the
ListView_ColumnSetWidth() function to specify the width of the column.
Specifying the LV_AUTO_SIZE as the column width will size the column so
the widest column item will be displayed without truncating. If you are
setting the width of the last column in the list view, use the LV_AUTO_FIT
constant to resize the column to fill the remaining space in the list view.
140 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 1 3 L I S T V I E W
For example, the following illustration shows a list view field that allows
multiple selections. The ListView_SelectionCount() function indicates that
three items are selected. To retrieve the index for the item “4750 9th Avenue
South”, pass the offset value 2 to the ListView_SelectionGet() function to
retrieve the second selected item.
To retrieve this
item, use 2 as the
offset value.
142 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 1 3 L I S T V I E W
else
{Only have to look at the item.}
if pos(ListView_ItemGetLabel(listview_field, current_item),
➥ search_string, 1) <> 0 then
{The item was found.}
found = true;
item_index = current_item;
end if;
end if;
current_item = current_item + 1;
end while;
146 P R O G R A M M E R ’ S G U I D E V O L U M E 2
Chapter 14: Table Overview
Dexterity is a database development tool, so the most common use of
scripts is to work with data in tables. The following topics are discussed:
Working with tables is one of the most difficult concepts to grasp while learning
Dexterity; you may not understand everything your first time through. The best
approach is to carefully work through the examples provided and then try the
concepts in your own application.
Data in windows
The data you see displayed in windows comes from entries users make in
windows or from tables that are part of the application. Users can change
information in a window without affecting data in tables. Information in a
window is maintained until the form containing the window is closed;
opening or closing a particular window doesn’t change information
displayed in a window.
Scripts are used to move information from the fields in windows to the
fields stored in tables. Dexterity provides special sanScript commands that
make moving data between windows and tables much easier.
Table buffers
A buffer is a temporary storage area in memory. Each time you attach a
table to a form, a table buffer is created for the table. If you attach a table to
more than one form, each form will have its own table buffer for that table.
A table buffer can hold one record of information. The information in the
table buffer comes from either windows that are part of the form or from
the table, depending on whether you’re reading a record from the table or
storing the information entered in a window.
The relationship among forms, windows, table buffers and tables is shown
in the following illustration.
Form A Form B
Windows
Table buffers
Table 1 Table 2
Note that there is a table buffer for each table attached to a form, and that
Table 2 is attached to both Form A and Form B, so each form has a table
buffer for Table 2.
148 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 1 4 T AB L E O V E R V I E W
Form: Address_Maintenance
Window
Name:
Address:
Table buffer City:
State:
ZIP Code:
Table
Address_Data
152 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A PT E R 1 5 W O R K I N G W IT H R E C O R D S
Saving a record
When saving information from a window in a table, the user must first
enter the information in the window, in this case, the address information
for A-1 Construction. When all the information is entered, the user can click
the Save button in the window to save the information in the table. The
following script, attached to the Save button, performs two basic steps to
save the information.
{Step 1: Copy the information from the window to the table buffer.}
copy to table Address_Data;
{Step 2: Save the information in the table buffer to the table.}
save table Address_Data;
{Clear the window to accept another record.}
restart form;
Window
copy to
table 1
Table
Address_Data
Retrieving a record
Retrieving a record is a multi-step process. The user must enter the key
values for the record; in this case, the name A-1 Construction. Then the
following script, a change script for the Name field, retrieves the record and
copies the information to the window.
More information {Release the lock on any record currently in the table buffer.}
about record locking release table Address_Data;
can be found in {Step 1: Copy the key field from the window to the table buffer.}
Chapter 18, “Multiuser 'Name' of table Address_Data = 'Name' of window Address_Maintenance;
processing.”. {Step 2: Retrieve the record from the table, allowing the data to be
changed.}
change table Address_Data;
{Step 3: Copy the information to the window.}
copy from table Address_Data;
Window
set 1
Table
Address_Data
154 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A PT E R 1 5 W O R K I N G W IT H R E C O R D S
Window
copy
from 3
table
Name: A-1 Construction
Address: 1234 Main Street
Table buffer City: Springfield
State: NE
ZIP Code: 55555-0321
change
2
Table
Address_Data
The change statement retrieves the record from the file, based upon the key
value set in the table buffer. In this example, the record for A-1 Construction
was retrieved because the key value was set to A-1 Construction. The copy
from table statement copies the record from the table buffer to the window
so the information is displayed.
Deleting a record
To delete a record, the record must exist in the table and be retrieved using
the “Retrieving a record procedure” just described. Then the record can be
removed from the table. The following script, attached to the Delete button,
will remove the record currently in the table buffer from the table.
{Remove the record currently in the table buffer from the table.}
remove table Address_Data;
{Clear the window to accept another record.}
restart form;
Customer_Maintenance form
Windows
Table buffers
Tables
Customer_List Customer_Comments
The application uses two tables, each with the following fields:
Customer_List Customer ID
Name
Address
City
State
ZIP
Phone Number
Customer_Comments Customer ID
Comments
156 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A PT E R 1 5 W O R K I N G W IT H R E C O R D S
Saving a record
When saving information from a window to a table, the user must first
enter the information in the form’s windows. When all the information is
entered, the user can click the Save button in the window to save the
information to the tables.
Refer to the SanScript To save information entered in windows into tables, the information must
Reference manual for be copied from the windows to the appropriate table buffers. To simplify
more information this process, Dexterity uses multiple table auto-copy. Multiple table auto-copy
about the copy from allows you to transfer all of the information from a table buffer to all the
table and copy to windows in the form using the copy from table statement, or from all
table statements. windows in the form to the table buffer using the copy to table statement.
Data will be copied between the corresponding window and table fields
(those having the same resource ID).
The AutoCopy property for each window field controls whether the field
will be updated by the copy from table and copy to table statements. By
default, the AutoCopy property is set to true.
The copy to table statement must be used carefully when more than one
window in the form contains the same table field. (In this example, the
Name field from the Customer_List table is used for both the
Customer_Maintenance and Comments windows.) The copy to table
statement will copy the fields from the main window to the table buffer,
then copy fields from any child windows (windows in the form other than
the main window) to the table buffer. This means the contents of the
common field in the child window will be copied to the table buffer,
replacing any values copied from the field on the main window. This will
occur even if the child window isn’t open.
The following script, attached to the Save button, performs the steps
necessary to save the information. The illustration on the following page
shows each step.
158 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A PT E R 1 5 W O R K I N G W IT H R E C O R D S
1
set
2 copy to
table
save
3 table
Customer_List Customer_Comments
4 copy to
table
save
5 table
Customer_List Customer_Comments
Retrieving a record
Retrieving a record is a multi-step process. The user must enter the key
values for the record; in this case, the Customer Number 104 for April
Berger. Then the following script, a change script for the Customer ID field,
retrieves the correct records from the Customer_List table and the
Customer_Comments Table and copies the information to the
Customer_Maintenance and Comments windows.
{Step 1: Copy the key field from the window to the Customer_List table
buffer.}
'Customer ID' of table Customer_List = 'Customer ID' of window
➥ Customer_Maintenance;
{Step 4: Copy the key field from the window to the Customer_Comments
table buffer.}
'Customer ID' of table Customer_Comments = 'Customer ID' of window
➥ Customer_Maintenance;
160 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A PT E R 1 5 W O R K I N G W IT H R E C O R D S
1 set
2 change
Customer_List Customer_Comments
copy from
3 table
Customer_List Customer_Comments
4 set
5 change
Customer_List Customer_Comments
copy from
6 table
Customer_List Customer_Comments
162 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A PT E R 1 5 W O R K I N G W IT H R E C O R D S
Deleting a record
To delete a record, the record must exist in the table and be retrieved as
described in the previous section. Then the record can be removed from the
table. The following script, attached to the Delete button, will remove the
records for the current customer from the Customer_List and
Customer_Comments tables.
Adding records
To allow multiple records to have the same key value, you must choose the
appropriate options in the Key Definition window when you create keys for
the table. For Pervasive.SQL and c-tree tables, mark the Duplicates option.
For SQL tables, be sure the Unique option is not marked. If you haven’t
chosen the appropriate options, an error indicating a duplicate record will
occur when you add records with the same key value to a table.
Retrieving records
It’s more difficult to retrieve records that have the same key value from a
table. The standard practice of setting the table buffer to the key value and
then using a get or change statement won’t work because the database
manager can’t guarantee which record will be retrieved. For example, if the
table buffer for the table shown in the previous illustration was set to
“Smith” and a get or change statement was used to retrieve a record, the
table buffer could contain the record for Alan, Maria, Bob or Sharon Smith.
There is no way of knowing which record will be read.
Two methods can be used to reliably retrieve records that have the same
key value.
• Start at the beginning of the table using the get first or change first
statement. Then use get next or change next statements to read all of
the records in the table. Be sure you’re using the same key for the get
first or change first and get next or change next statements.
This method will read all of the records in the table, including those
that have the same key value. However, there is no way of determining
the order in which the records having the same key value will be read.
• Use the range start statement to set the beginning of the range to the
duplicate key value, and use the range end statement to set the end of
the range to the duplicate key value. Use the get first or change first
statement to retrieve the first record in the range. Then use the get next
or change next statement to retrieve successive records from the range.
Be sure you’re using the same key for the range and get or change
statements.
This method will read all of the records that have the same key value
specified using the range statements. However, there is still no way of
determining the order in which the records having the same key value
will be read.
164 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A PT E R 1 5 W O R K I N G W IT H R E C O R D S
The following script reads all of the “Smiths” from the example table. The
range statements allow the table to access only records with the key value
“Smith.” The get first and get next statements read all of records from the
range in the table. Note that the same key is used for all of the range and get
statements.
The value of the key field Last Name was changed from Schulz to Smith
and the record was saved using the save table statement. If the database
manager being used allows modifying key values, the record would be
saved. The record would then contain the new last name as shown in the
following illustration.
166 P R O G R A M M E R ’ S G U I D E V O L U M E 2
Chapter 16: Ranges
When working with tables, it is often efficient to limit the amount of
information being accessed. You can do this by setting up a range for the
table. Information about ranges is divided into the following sections:
• Range overview
• Ranges for multisegment keys
• Range types
• Well-behaved ranges
Range overview
A range is based upon a key for the table, and allows you to access a
specified portion of the table. The selected range will be treated as an entire
table. For instance, a get first statement returns the first record in the range,
a get last statement returns the last record in the range, and so on.
The range statement is used to create a range for a table. You can specify
one range per table, and the range is associated with a specific key. The
range will be used only when the table is accessed by the key the range is
associated with.
In the following example, the range statement is used to limit the records
accessed to only those for which the Selling Price is between 500 and 1000
dollars. Notice that the same key, composed of the Selling Price field, is
used for each of the range statements as well as the get first statement.
You can use the Table_IsRangeSet() function to find out whether a range is set for
a table.
The following script sets a range to include all purchases made on 11/17/98
for all stores. The first segment of the key is set to the date 11/17/98. The
second segment is set to its minimum value using the clear field statement,
then the start of the range is set. The first segment remains 11/17/98. The
second segment is set to its maximum value using the fill statement. Then
the end of the range is set. Using the clear field and fill statements on the
Store ID fields allows all stores to be selected.
168 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 1 6 R AN G E S
Range types
Refer to the range How a range is evaluated depends upon the database manager used for the
statement in the table. For the Btrieve and c-tree database managers, ranges are evaluated
SanScript Reference inclusively. For the SQL database manager, ranges are evaluated either
manual for a complete inclusively or exclusively.
description of how
Dexterity evaluates The following example illustrates the difference between these two
ranges for the methods of evaluating a range. The sample table shown in the following
Pervasive.SQL, c-tree illustration has a key composed of the three segments shown.
and SQL database
managers. Segment 1 Segment 2 Segment 3
A A A
A A B
A A C
A B A
A B B
A B C
A C A
A C B
A C C
B A A
B A B
B A C
B B A
B B B
B B C
B C A
B C B
B C C
C A A
C A B
C A C
C B A
C B B
C B C
C C A
C C B
C C C
Range start: A A A
Range end: A C B
Refer to the range The following records are included in the inclusive range.
statement in the
SanScript Reference Segment 1 Segment 2 Segment 3
manual for a complete A A A
description of how
A A B
inclusive and exclusive
A A C
ranges are evaluated.
A B A
A B B
A B C
A C A
A C B
Notice that inclusive and exclusive ranges don’t contain the same records.
This is an issue when developing an application, because the Btrieve and c-
tree database managers always produce inclusive ranges, but the SQL
database manager has optimal performance when it produces exclusive
ranges. The SQL database manager can be forced to produce inclusive
ranges, but the application’s performance may be seriously degraded. To
eliminate the discrepancies that result from the two types of ranges, we
recommend that you implement “well-behaved” ranges in your
application.
170 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 1 6 R AN G E S
Well-behaved ranges
A “well-behaved” range has the following characteristics:
• Beginning with the leftmost key segment and working to the right, the
first 0 to n segments are set to equal values for both the range start and
the range end.
• The next 0 or 1 segments are set to non-equal values for the range start
and range end.
• The remaining segments (if any) are cleared for the range start and
filled for the range end.
The scripts in the first and second examples of this section both create
“well-behaved” ranges. The ranges produced will be the same, regardless
of the database manager being used.
User_Name\Local Settings\Temp\TNTxx
While memory-based tables were slightly faster than disk-based tables, you
won’t see a significant change in performance with this change. The change
was necessary due to the number of large memory-based tables that were
being used at one time. As memory became fragmented, no contiguous
blocks large enough could be allocated to complete table operations. In
some cases, this problem caused application errors that affected end-users.
The change to disk-based tables eliminates this issue.
You can also use the open table statement to open an existing table as a
memory-based table.
If you don’t want a memory-based table to have path information, make the
table part of the Pathname series. The Pathname script doesn’t run when
tables in the Pathname series are accessed.
• Memory-based tables remain in the current user’s Temp folder until the
application is shut down or the table is removed using the delete table
statement. If the application shuts down unexpectedly, the .DAT and
.IDX files for the table will remain on the user’s disk until manually
removed.
• Memory-based tables have shared access. This means that the table can
be accessed from multiple locations in the application simultaneously.
For example, three separate forms and a procedure could all access a
single memory-based table. Remember that the path information must
be the consistent to access the same memory-based table.
174 P R O G R A M M E R ’ S G U I D E V O L U M E 2
Chapter 18: Multiuser processing
Dexterity supports multiple users accessing the same table at the same
time. To accommodate this, Dexterity applications apply Optimistic
Concurrency Control (OCC), a form of record locking that allows multiple
users to work in the same tables and access the same records with minimal
restrictions, while helping to ensure data integrity.
• Record locking
• Releasing locks
• Coding a multiuser application
• Multiuser examples
Record locking
A record must be locked to delete it or save any changes made to it. A lock
is applied when a record is read from a table. Two types of locks can be
used: passive and active.
Passive locking
A passive lock allows other users to access the record. Other users can delete
the record or make changes to it. Passive locking ensures that other users
accessing the record can be made aware that the record has been deleted or
that the contents of the record have changed. A passive lock is applied each
time a record is read using the change or edit table statement.
Active locking
An active lock allows other users to read the record, but not make any
changes or delete the record. Active locking ensures that the user who has
the active lock is the only user who can make changes or delete the record.
If other users try to delete or change the record, a table-sharing error will
occur. An active lock is applied each time a record is read using the change
or edit table statements with the lock keyword included.
The get statement is used only to read a record. It never locks a record.
You must mark the Allow Active Locking option in the table definition for
each table on which active locking will be allowed. If you don’t mark this
option, any change or edit table statements that reference that table and
include the lock keyword will produce errors when you attempt to compile
them.
We strongly recommend that you avoid using active locking in your application.
Application performance can degrade when active locking is used, especially when
your application uses a SQL database.
Releasing locks
Any of the following actions releases a record lock:
The following table lists the scenarios that can occur. The events listed
happen in order from left to right. For example, in the first row User A
passively locks a record, then User B passively locks the same record. User
A deletes the record, then User B changes the contents of the record and
saves the record. The changes User B made will be saved.
176 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 1 8 M U L TI U S E R P R O C E S S IN G
In the current version of Dexterity, fields with the text control types should not be
used in tables that are checked for multiuser error conditions. Records can’t be
locked properly if text fields are included in the table. If your application uses text
fields, store them in a separate table that won’t be checked for multiuser error
conditions.
The err() function is used to trap errors so the script can deal with the errors
that occur. The following examples are the Part Number, Save Button and
Delete button scripts for the sample inventory application. They use the
err() function to trap and handle errors.
Multiuser examples
The following examples show how to write the scripts to handle basic
multiuser scenarios. The first example shows how to read a record, the
second shows how to save a record, and the third shows how to delete a
record.
Example 1
Refer to the SanScript The following is the Part Number change script that retrieves and actively
Reference manual for locks a record. It uses the err() function to handle an error resulting from the
more information record being actively locked by another user.
about the err()
function. {Release the lock on any record currently in the table buffer.}
release table 'Inventory_Data';
{Set the value of the key field in the table buffer.}
'Part Number' of table Inventory_Data = 'Part Number' of window
➥ 'Inventory_Maintenance';
{Attempt to retrieve the record from the table and lock it.}
change table 'Inventory_Data' by Inventory_Data_Key1, lock;
if err() = OKAY then
{If there was no error, the record was successfully read.}
copy from table 'Inventory_Data';
clear changes form 'Inventory_Maintenance';
{Enable the Delete button and lock the Part Number field.}
enable 'Delete Button';
lock 'Part Number';
178 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 1 8 M U L TI U S E R P R O C E S S IN G
Example 2
The following is the Save button change script. It uses the err() function to
handle an error resulting from the record being changed or being actively
locked by another user.
if required(form Inventory_Maintenance) then
copy to table Inventory_Data;
save table Inventory_Data;
if err() = RECORDCHANGED then
{The record was changed by another user.}
warning "This record has been changed by another user.
➥ The record will be read again.";
change table Inventory_Data;
copy from table Inventory_Data;
elseif err() = LOCKED then
{The record is actively locked by another user.}
warning "This record is currently locked by another
➥ user. Changes will not be saved";
else
{Save was successful.}
restart form;
end if;
else
warning "Please enter all required fields.";
end if;
After the successful save operation, the save table statement automatically
updates the fields in the table buffer that were changed by other users. This
means that the values in the table buffer after the save operation may differ
from the values in the table buffer before the save operation. After a
successful save operation, you may want to update the data displayed in
your application to reflect these changes.
Example 3
The following is the Delete button change script. It uses the err() function to
trap and handle an error resulting from the record being actively locked by
another user.
if ask("Delete this item?","Cancel","Delete","") = ASKBUTTON2 then
remove table Inventory_Data;
if err() = LOCKED then
{The record is actively locked by another user.}
warning "This record is currently locked by another user.
➥ It can't be deleted.";
elseif err() = MISSING then
{The record has been deleted by another user. No need to
delete record.}
restart form;
else
restart form;
end if;
end if;
When you’re writing scripts that handle errors using the err() function, you may
want to use the check error statement as a debugging tool while you’re working.
The check error statement will display a message indicating the type of table error
that occurred.
180 P R O G R A M M E R ’ S G U I D E V O L U M E 2
Chapter 19: Transactions
Transactions are a database feature that allow you to perform a set of table
operations as a single unit. Transactions are useful when you have sets of
table operations that must all be completed for the integrity of the data to be
maintained. Partial completion of the operations would leave the database
in an inconsistent state.
• Transaction overview
• Error handling for transactions
• Transaction template
• Guidelines for transactions
• Integrating applications
Transaction overview
In Dexterity, transactions are supported only for the Microsoft SQL
Server™ database. You can’t use transactions with any of the other
supported database managers. Use the havetransactions() function to find
out whether the default database type supports transactions.
Once a transaction is started, any scripts that are called are also part of the
transaction. For instance, any database operations performed by
procedures called from within a transaction are part of the transaction.
The most common recoverable error that occurs when using a transaction is
a deadlock. A deadlock occurs when two users, each having locked a
record, attempt to lock the other user’s record. Each user waits for the other
to release the lock. If a deadlock occurs, an exception will be thrown that
your application will need to handle.
Transaction template
This section contains sample sanScript code that you can use as a template
when adding transaction support to your application.
Operation
This sample code is designed to execute a set of sanScript statements within
a transaction. If transaction processing isn’t available for the current
database type, or if a transaction is already in progress, the sanScript
statements will still be executed, just not within a transaction.
182 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A PT E R 1 9 T R A N S AC TI O N S
• If you can’t be certain of the state of a table buffer before you lock a
record, it’s a good idea to use the release table statement to ensure a
record isn’t already locked.
• If you use any c-tree temporary tables within the transaction, you will
need to perform any clean-up operations for them when the transaction
has finished. You will also need to do this if the transaction is rolled
back, restarted or terminated abnormally.
Refer to Guidelines for transactions on page 185 for more information about
designing the code to be executed within the transaction.
retry_count = 0;
try
{If a transaction can be used, start one.}
if (Tools_GetTranLevel() = 0) and (havetransactions()) then
transaction begin;
transaction_started = true;
else
transaction_started = false;
end if;
catch [EXCEPTION_CLASS_DB_DEADLOCK]
{If a transaction was started, it was automatically rolled back.}
184 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A PT E R 1 9 T R A N S AC TI O N S
else
{Some other unexpected exception occurred. If the transaction
this code started is still pending, roll it back.}
if transaction_started = true then
transaction rollback;
end if;
• If you use the call sproc statement within a transaction, the operations
performed by the stored procedure are considered part of the transac-
tion. If a transaction is active, and the stored procedure called attempts
to start another transaction, an exception will be thrown indicating the
transaction level changed.
Integrating applications
Transactions are implemented throughout Microsoft Dynamics GP. In most
cases, integrating products won’t need to deal with transaction issues. If
your integrating application registers a trigger that is activated within a
Microsoft Dynamics GP transaction, any table operations your script
performs will become part of the transaction. If the transaction is
committed, your database operations will be committed. If the transaction
is rolled back, your database operations will be rolled back.
186 P R O G R A M M E R ’ S G U I D E V O L U M E 2
PART 5: ADVANCED SCRIPTING
Part 5: Advanced Scripting
This part describes advanced scripting topics and features available in
Dexterity. Following is a list of the topics that will be discussed, with a brief
explanation of each:
• Chapter 23, “Controlling the Compiler,” describes how to use the script
preprocessor in Dexterity.
• Chapter 24, “Call Stacks,” explains what call stacks are and what a pro-
grammer must know to manage them properly.
188 P R O G R A M M E R ’ S G U I D E V O L U M E 2
Chapter 20: Procedures
A procedure is a script that provides functionality common to several parts
of an application. Use of procedures can eliminate duplication of
programming efforts and reduce application maintenance. Information
about procedures is divided into the following sections:
• Terminology
• Creating and calling procedures
• Parameters
• Using parameters
• Optional parameters
• Passing parameters to other procedures
• Using global fields as parameter types
• Table buffers
• Form procedures
• Anonymous tables and fields
• Background and remote processing
• Process groups
Terminology
Unlike other scripts, procedures aren’t attached to fields, windows or
forms; instead, procedures are called from other scripts in an application.
When a procedure is called from another script, the two scripts involved
have specific roles:
• The calling script is the script that accesses, or calls, the procedure. The
calling script can be any other script in the application.
• The called script is the procedure that is invoked by the calling script.
A set of parameters can be used to pass data to the called script or return
data to the calling script.
The call and call with name statements temporarily transfer control to a
procedure, passing an optional set of parameters. Refer to the SanScript
Reference manual for complete documentation for the call and call with
name statements.
Parameters
Procedures operate by passing parameters between the calling script and
the procedure. The procedure uses the information in these parameters to
work with variables, fields and tables, and to return values to the calling
script. Three types of parameters can be used, as shown in the following
illustration.
in
out
inout
Parameters are classified based upon how they are used when the calling
script communicates with the called script. The following table describes
the different types.
Type Characteristics
in The value is passed from the calling script to the called script. The
called script can’t change the value of an in parameter.
out The value is passed from the called script back to the calling script.
The called script sets the value of the out parameter.
inout The value is passed from the calling script to the called script and
then back to the calling script. The value can be used and changed
by the called script, which then passes it back to the calling script.
190 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A PT E R 2 0 P R O C ED U R E S
Typically, the parameters passed to and from a procedure have one of the
standard storage types: boolean, currency, variable currency, date, integer,
long, string and time. You can also use existing global field definitions in a
dictionary when declaring parameter types. This method is used when
passing items such as composites to a procedure.
Using parameters
To effectively use procedures, you must develop an understanding of how
in, out and inout parameters are used.
In parameters
In parameters are used to pass values from the calling script to the called
script. In the called script, the value of the in parameter can only be
referenced. You can’t alter the value of an in parameter. The following
example shows how to use an in parameter in a procedure.
Example 1
This procedure warns when a customer is over his or her credit limit:
in currency limit;
in string name;
Out parameters
Out parameters are used to pass values back to the calling script from
the called script. The called script sets the value of the out parameter. The
calling script can only reference the out parameter; it can’t change
the parameter’s value. The following example shows how to use an out
parameter in a procedure.
Example 2
The following is a procedure that returns a string indicating the discount
percentage to apply, based upon the purchase amount:
in currency purchase_amount;
out string discount_percent;
192 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A PT E R 2 0 P R O C ED U R E S
Inout parameters
Inout parameters allow values to be passed from the calling script to the
called script, and then passed back to the calling script. Both the calling and
the called script can reference and alter the value of an inout parameter. The
following examples shows how to use an inout parameter in a procedure.
Example 3
The following procedure receives a purchase amount through an inout
parameter, modifies the value based upon the amount of the purchase, and
returns the value to the calling script.
Optional parameters
To make a parameter optional, simply include the optional keyword when
the parameter is declared. The following is a declaration of an optional in
parameter.
optional in string item_description;
If you declare an optional in parameter, you can specify the default value to
use if the calling script doesn’t supply a value. For example, the following
parameter declaration specifies that the initial_quantity parameter will
have the value 1, unless the user supplies another value.
optional in integer initial_quantity = 1;
You can use the missing() function to find out whether optional in, inout or
out parameters contain values.
Example 4
This example checks whether the optional description parameter is
supplied. If the value is supplied, the value is displayed in the Description
field. Otherwise, the value “None” is displayed.
in string item_name;
optional in string description;
194 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A PT E R 2 0 P R O C ED U R E S
When you call a procedure that has several optional parameters, you may
not want to supply values to all of them. Use a set of double commas to
indicate that no value is supplied to an optional parameter. For example,
the second and third parameters of the Calculate_Taxes procedure are
optional. The following call to the procedure doesn’t supply a value to the
second parameter.
If you aren’t supplying values to optional parameters that appear at the end
of the parameter list, you can simply omit the parameters. For example, the
following call to the Calculate_Taxes procedure doesn’t supply a value to
the third (last) parameter, so the parameter is omitted.
When a value is passed into a procedure, you may want to pass the
parameter in a call to another procedure. There are restrictions on the type
of parameters that you can pass to other procedures. The following is a list
of the types of parameter passing that are allowed.
in in
Procedure A Procedure B
out out
Procedure A Procedure B
in, out, or
inout inout
Procedure A Procedure B
196 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A PT E R 2 0 P R O C ED U R E S
Example 5
This example procedure uses two composites as in parameters. The
composite parameter type is based upon a composite global field, Account
Number, defined for the application. The procedure is passed two
composites as in parameters and returns a string value indicating whether
the first is equal to the second.
Using global field definitions as parameter types has another use. The
global field definition for a window field can be used as a parameter type.
This allows you to pass a specific window field, such as a list box, to a
procedure as an inout parameter and manipulate it directly in the
procedure. For example, you can show, hide, enable, disable, lock or unlock
a field passed into the procedure in this manner.
Example 6
This procedure declares a parameter type based upon the Shipping Method
global field definition. The Shipping Method field is a list box. The
Shipping Method field is passed into the procedure, an item is added to the
list, and the list is redrawn.
Table buffers
Each procedure has its own table buffer for each table in the application.
This allows a procedure to access any table in the application. The
procedure’s table buffers exist only as long as the procedure is running.
Example 7
This example shows how a table buffer is passed to and used in a
procedure. The table buffer passed is the table buffer used by the form
where the calling script is located. The table buffer must be passed as an
inout parameter.
The following script calls the Balance_Warning procedure, passing the table
buffer for the Customer_Data table to the procedure. The Customer Name
field in the table buffer is used in the warning message.
call Balance_Warning, table Customer_Data;
198 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A PT E R 2 0 P R O C ED U R E S
Example 8
This example counts records in a named table. A table name is passed into
the procedure as a string. A local anonymous table is declared. The table is
opened using the open table statement and the with name clause. The
procedure returns the number of records in the table as an out parameter.
in string table_name;
out long record_count;
Local anonymous tables don’t allow you to have several instances of the same table
open in a procedure. They allow you only to open a table based upon a name that is
specified at runtime.
Form procedures
Refer to Chapter 24, Form procedures are functionally the same as standard procedures, except
“Call Stacks,”, for they’re associated with a specific form. Form procedures can be used to
information about why group procedures that perform tasks associated with a specific form. They
form procedures can also be used to take the place of scripts called with the run script
should replace scripts statement, and when additional table buffers are needed for a form. Form
called with run script. procedures use their own table buffers, not the form’s table buffers.
Example 9
The following statement calls the form procedure Delete_Customer_Record
attached to the Customer_Maintenance form.
call Delete_Customer_Record of form Customer_Maintenance;
200 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A PT E R 2 0 P R O C ED U R E S
Anonymous table
parameter in the in anonymous table source_table;
procedure.
Example 10
The following procedure builds a master mailing list from several different
tables with known fields. The procedure has an anonymous table as an
inout parameter. All of the tables that will be passed to the procedure must
have fields with names Name, Address, City, State, and ZIP Code. Note that
in the procedure, the table is referenced using the name declared by the
inout parameter line at the top of the script. The procedure takes the fields
from the anonymous table and writes them to the Mailing_List table.
Example 12 shows how this procedure is called and the anonymous table
parameter is passed to the procedure.
You can use the datatype() function to find out the data type associated with an
anonymous field.
Example 11
The following procedure builds a master mailing list from records in an
invoice table. The procedure has an anonymous table as an inout parameter
and anonymous fields as in parameters. The tables that will be passed to the
procedure will have address information but won’t necessarily have fields
named Name, Address, City, State and ZIP Code. Note that in the
procedure, the table and fields are referenced using the names declared by
the parameter lines at the top of the script. The procedure uses the
anonymous fields from the anonymous table to build the mailing list.
202 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A PT E R 2 0 P R O C ED U R E S
Example 12
The following script uses the procedures in the previous two examples to
build the Mailing_List table. The first two lines delete the existing
Mailing_List table. Addresses are then added from the Customer_List table
by passing it to the Add_to_Mailing_List procedure.
Next, addresses are added from the Invoice_Data table by passing the table
and its corresponding fields as anonymous parameters to the
Add_From_Invoice procedure. Note how fields from the Invoice_Data table
correspond to fields in the Add_From_Invoice procedure. For example, the
Customer Name field in the Invoice_Data table corresponds to the
Name_Field in the Add_From_Invoice procedure.
Background procedures
Background processing allows a procedure to run while other processing is
occurring, such as opening windows, printing reports and so on. To run a
procedure in the background, include the background keyword in the call
statement. This will add the procedure to a processing queue that will
process it when time is available.
Remote procedures
Refer to Chapter 12, You can also process procedures remotely by sending them to process
“Process Server,” in the servers. To send a procedure to a process server, you must set up a process
Dexterity Stand-alone group and include the remote keyword in the call to the procedure. Process
Application Guide for groups are described in the next section.
more information
about using a process Only procedures meeting certain criteria can be sent to a process server. For
server. instance, the procedure can’t generate any dialog boxes or messages since
no users will be at the server to respond to the message.
204 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A PT E R 2 0 P R O C ED U R E S
Process groups
Process groups allow you to define a set of procedures that must be
completed in a designated order, without interruption from another
process. This allows a group of related procedures, such as those used to
post transactions, to be processed or deleted as a single group. To set up a
process group, use the begingroup and endgroup statements. These
statements define the beginning and ending points of a process group. The
basic syntax for a process group is shown below:
Process Queue
1 Procedure_1 Procedure
Procedure_2
Order of 2 Procedure_3 Process Group
completion Procedure_4
3 Procedure_5 Procedure
Procedure_6
4 Process Group
Procedure_7
• Once in a process group, all procedures in the process group are treated
as a single unit. Actions made to the group, such as moving, deleting
and so on, affect all procedures in the group.
206 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A PT E R 2 0 P R O C ED U R E S
The name you give the function script in the script editor is the same name that will
be used when the function is called. You don’t need to include the parentheses in the
name. We also recommend that this name not contain any spaces. If the name is
more than one word, substitute underscores (_) for spaces in the name.
The first non-comment line of the function indicates what the function will
return. It has the following format:
For more information The type parameter indicates the type of value returned by the function.
about using global Typically, this is one of the seven standard storage types: boolean, currency,
field definitions as date, integer, long, string or time. You can also use existing global field
parameter types, refer definitions in your application dictionary to specify the return value type.
to Example 5 in This is useful if you want to return something such as composite from the
Chapter 20, function.
“Procedures.”
The variable parameter indicates the variable that returns the value from the
function. This value is returned automatically when the function script
finishes. If you don’t specifically set the value of the return variable, it will
be returned with the “empty” value for that type. (For example, an integer
return value that didn’t have its value set would be returned as 0.)
If you don’t want the function to return any value at all, use the keyword
nothing to indicate that the function doesn’t return a value. The first line of
the function would be:
Refer to Chapter 20, The parameters for the function follow the first line. The parameters are
“Procedures,” for optional; you can create a function that doesn’t use any parameters. Any
information about parameters for the function are declared and operate the same way that
procedure scripts. parameters do for procedures. With the exception of the return value, a
function script has the same characteristics as a procedure, such as access to
all tables in the application dictionary.
Example 1
The following example shows a function script that is used to generate
random numbers between 0 and 1. The function requires that a long integer
field named seed be defined as a system variable. The following constants
have also been defined using the Constant Definition window:
MODULUS = 2147483647
MULTIPLIER = 16807
QUOTIENT = 127773
REMAINDER = 2836
210 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A PT E R 2 1 U S ER - D E F I N E D F U N C TI O N S
{Keep the current seed. This will be used to get the next random
number in the stream.}
temp = seed of globals;
{Generate the next seed.}
hi = seed of globals / QUOTIENT;
low = seed of globals % QUOTIENT;
test = (MULTIPLIER * low) - (REMAINDER * hi);
if test > 0 then
seed of globals = test;
else
seed of globals = test + MODULUS;
end if;
random_number = temp * (1.0) / MODULUS;
seed of globals = 1;
result = random();
Example 2
This example shows a function script named Time_AddSeconds that is
used to add seconds to a time value. The Time_AddSeconds() function
takes a time value and an integer as parameters, and returns a time value.
t_minute = minute(original_time);
t_second = second(original_time);
{Add the seconds to the time.}
if t_second + add_amount > 59 then
t_second = (t_second + add_amount) % 60;
{Need to increment the minutes.}
if t_minute + 1 > 59 then
t_minute = (t_minute + 1) % 60;
{Need to increment the hours.}
if t_hour + 1 > 23 then
t_hour = 0;
else
t_hour = t_hour + 1;
end if;
else
t_minute = t_minute + 1;
end if;
else
t_second = t_second + add_amount;
end if;
result_time = mktime(t_hour, t_minute, t_second);
end if;
212 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A PT E R 2 1 U S ER - D E F I N E D F U N C TI O N S
Form functions
Form functions operate like global user-defined functions, except they are
associated with a specific form. Form functions are used to provide
functionality that is specific to a form. Form functions use their own table
buffers, not the form’s table buffers.
Naming functions
We recommend that you preface the name of your functions with an
identifier indicating the developer who defined the function. For example,
if your company is Sophisticated Software Incorporated, use some
identifier such as SSI, at the beginning of your function names. This will
help avoid confusing your functions with sanScript functions and functions
from the Dexterity function libraries.
Before you can use a DLL in your application, you must create a procedure
that will be used to pass parameters to the DLL. This procedure is used only
to specify the type of parameters that are required for the DLL to run
properly when called from another script. The procedure isn’t called in the
typical manner using the call statement; the extern statement is used
instead. The extern statement calls the procedure, using the DLL name and
passing the actual parameters you want to use with the DLL. No statements
in the procedure are executed because the procedure doesn’t actually run.
functionname@dllname.dll
The functionname is the name of the function in the DLL being called
and dllname.dll is the name of the DLL the function is part of.
216 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 2 2 D Y N A M I C L IN K L I B R A R I E S
Remember that the procedure name must include the name of the function
within the DLL, followed by the @ symbol and the name of the DLL to be
called. In order for the @ symbol to compile properly, the entire procedure
name must be enclosed in single quotation marks. The extern statement
also requires a return_value and a parameter list to handle the parameters
sent to and the values returned from the DLL.
When the DLL is called using the extern statement, it is loaded into
memory. Immediately after the DLL call is complete, Dexterity unloads the
DLL from memory. If the DLL must remain in memory, use the
Utility_LoadDLL() function to load the DLL into memory. When you have
finished calling the DLL, use the Utility_UnloadDLL() function to remove
the DLL from memory.
The following example is the script that displays a message using the DLL
and procedure set up on the previous example. Notice that the name of the
procedure script appears in single quotes, otherwise the script won’t
compile properly.
We recommend that you place any external DLLs you use in the same directory as
Dexterity or the runtime engine.
218 P R O G R A M M E R ’ S G U I D E V O L U M E 2
Chapter 23: Controlling the Compiler
You can use special syntax in your scripts to control the sanScript compiler.
Dexterity has a script preprocessor that allows you to selectively compile
portions of scripts. Selective compiling is useful when you have code that is
used for one version of your application, but not for other versions.
You can also use pragmas in your scripts. Pragmas are special instructions
that the sanScript compiler uses to control how it compiles scripts. For
example, you can control how the compiler responds to potential code
problems such as possible infinite loops.
Information about the controlling the compiler is divided into the following
sections:
• Script preprocessor
• Script preprocessor examples
• Pragmas
• Pragma reference
Script preprocessor
You use the script editor to add preprocessor directives to your scripts.
Preprocessor directives are commands used by the script preprocessor to
determine which portions of a script to compile. All preprocessor directives
start with a pound sign (#) in the first column of the script editor and end
with a carriage return. They do not end with a period. The preprocessor
directives for selective compiling have a structure much like the if
then...end if statement. The structure is shown in the following illustration.
Like the if then...end if statement, the #if and #end if clauses are required.
You can include one or more of the optional #elseif clauses. The #else
clause is also optional, but only one can be included.
Refer to Chapter 21, You can use literal values and constants when writing the expressions. Any
“Constants,” in Volume constants you reference must have been defined in the dictionary using the
1 of the Dexterity Constant Definition window.
Programmer’s Guide
for more information The expression clause can be a boolean expression that evaluates to true or
about defining false. It could also be an integer expression for which any non-zero value is
constants. considered true and zero is considered false. The script statements
following the first expression to be evaluated as true will be compiled.
Example 1
Refer to Chapter 4, This example describes how the script preprocessor can be used to
“Script Utilities,” in the selectively compile code for the SQL and non-SQL versions of an
Dexterity Utilities application. A constant named SQL has been added to the dictionary using
manual for information the Constant Definition window. If this constant is set to the value 0 and
about compiling all of scripts are recompiled, the non-SQL code is used. If the constant is set to 1
the scripts in your and scripts are recompiled, the SQL code is used. In this example, a
application. standard Pathname Setup form is opened when the non-SQL code is used.
When the SQL code is used, a SQL version of the Pathname Setup form is
opened.
#if SQL = 0
open form Pathname_Setup;
#else
open form SQL_Pathname_Setup;
#end if
220 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 2 3 C O N T R O L L I N G TH E C O M PI L E R
Example 2
This example describes how the script preprocessor can be used to
selectively include debugging code in a special “debug” build of an
application. A constant named DEBUG has been added to the dictionary. If
this constant is set to 0 and scripts are recompiled, the debug code isn’t
compiled. If the DEBUG constant is set to 1 and scripts are recompiled, the
debug code is compiled. In this example, the debug code displays any error
that occurred as a result of the change statement.
#if DEBUG
{Display the number of any table error that occurred.}
if err() <> OKAY then
warning "Debug: Table error " + str(err()) +
➥ " on change operation in Seller ID script.";
end if;
#end if
Using the script preprocessor to include or exclude debugging code has two
advantages. First, you have more control over what operations the
debugging code performs. This is more flexible than the debug statement,
which only displays messages in test mode. Second, when you choose not
to include debug code, it’s not compiled. This means it doesn’t use
additional space in the dictionary or increase a script’s execution time.
Pragmas
You can add pragmas to your scripts to control the sanScript compiler. All
pragmas begin with the keyword pragma() and use additional arguments
to specify the instruction to give to the compiler. For example, you can
specify how the compiler handles certain syntax issues, such as warnings
for literal strings.
The following script contains a literal string. When you compile this script,
a warning will be generated.
You can use a pragma to disable the warning generated for this script. The
following script contains the pragma to disable the warning.
When using a pragma to turn off a compiler warning, usually you need to
re-enable the warning before the end of the script. If you don’t, you will
generate another compiler warning indicating the situation.
Pragma reference
The following is a list of the pragmas available to control warnings
generated by the sanScript compiler. Each warning can be disabled by the
following code:
pragma(disable warning constant);
222 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 2 3 C O N T R O L L I N G TH E C O M PI L E R
LiteralStringUsed
The LiteralStringUse pragma setting turns off the warning generated by
using literal strings in scripts. The following example shows this setting.
PossibleInfiniteLoop
The PossibleInfiniteLoop pragma setting turns off the warning generated
when a possible infinite loop is detected.The following example shows this
setting.
end while;
pragma(enable warning PossibleInfiniteLoop);
TypeMismatch
The TypeMismatch pragma setting turns off the warning generated when a
possible loss of data is detected when performing assignments. The
following example shows this setting.
int_val = long_val;
UnusedVariable
The UnusedVariable pragma setting turns off the warning generated when
an unused variable or parameter is detected.The following example shows
this setting.
The UnusedVariable setting should not be enabled again before the end of the script.
It will apply only to the current script.
NoExecutableCode
The NoExecutableCode pragma setting turns off the warning generated
when a script with no executable code is detected. For the best runtime
performance, scripts without any executable code should be removed from
the application. However, there are special cases (such as trigger points)
that are appropriate to have non-executing code. Use the
NoExecutableCode pragma to suppress the compiler warning in these
cases. The following example shows this setting.
The NoExecutableCode setting should not be enabled again before the end of the
script. It will apply only to the current script.
224 P R O G R A M M E R ’ S G U I D E V O L U M E 2
Chapter 24: Call Stacks
A call stack is an internal Dexterity mechanism that manages the processing
of scripts. Dexterity has eight call stacks; seven are used for foreground
processing while the other is used for background processing. When the
scripts on a particular call stack have finished processing, the call stack is
available to be used again.
Foreground processes
An attached script is one that is attached to an object such as a form,
window or field. When an attached script is run, it uses one of the
foreground call stacks. Any procedures or functions called from the
attached script, and any subsequent calls from those procedures or
functions, are processed on the current call stack. For example, if a push
button change script is run, it uses a call stack. If any procedures or
functions are called by the push button change script, those scripts and any
procedures or functions they call are also processed on the same call stack.
BG 1 2 3 4 5 6 7
Background processes
All background processing is handled by the call stack dedicated to
background processing. Any calls to procedures or functions from a script
running in the background will also be processed by the call stack
dedicated to background processing.
Special processes
Two types of special processes use foreground call stacks: procedures that
are called automatically and functions that are called from calculated fields
in reports. Procedures that are called automatically include the Pathname
procedure, Security procedure, and procedures for the process server.
A function in a calculated
field uses one call stack.
Each procedure
automatically called by
Dexterity uses one call
stack. 1 2 3 4 5 6 7
BG
226 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 2 4 CA LL STAC KS
BG 1 2 3 4 5 6 7
How you use the run script statement determines how many call stacks are
required. If a single script executes several run script statements, only one
additional call stack is needed. This is shown in the following example.
Script name: A
Script A uses run script to start scripts B and C. Only one additional call
stack is used because once script A starts script B, script A is suspended.
Script B finishes and its call stack is available to process script C.
BG 1 2 3 4 5 6 7
Script A continues and run script
uses run script to start
script C. Because script B A C
is complete, that call
stack is available to
process script C.
BG 1 2 3 4 5 6 7
If run script statements are “nested,” the likelihood of using all of the call
stacks increases. For example, if script A uses run script to start script B,
which uses run script to start script C, three call stacks are used.
Script name: A
Script name: B
BG 1 2 3 4 5 6 7
If enough nested run script statements are executed, all available call stacks
will be used. If you then execute another nested run script statement, try to
run an attached script, print a report that contains a calculated field which
uses a function, or cause one of the automatically executed procedures to
execute (such as by opening a table which causes the Pathname procedure
to run), you will receive the message “All call stacks are in use. Can’t start
script.”
All call stacks will be run script run script run script run script run script run script
used if a script executes
a run script statement A B
which in turn executes a
run script statement,
and so on.
BG 1 2 3 4 5 6 7
If you use run script in your application, keep track of the number of call
stacks that you will be using. If other processes will require call stacks, such
as the Pathname or Security procedures, be sure to leave enough call stacks
available.
228 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 2 4 CA LL STAC KS
We recommend that you don’t nest run script statements more than three levels
deep. If you exceed this number, your application may run out of call stacks.
One way to reduce the number of call stacks your application uses is to
move functionality to a procedure script instead of using a field change
script started by the run script statement.
Another possibility is to use the run script delayed statement. The run
script delayed statement runs a field change script after all foreground
scripts have finished. Then the first call stack is available to process
the script that was started using the run script delayed statement. This is
shown in the following illustration.
BG 1 2 3 4 5 6 7
After script A has finished,
script B is executed in the B
first call stack.
BG 1 2 3 4 5 6 7
For instance, you can create a reference to a specific table buffer. Then in
your sanScript code, you can use the reference instead of accessing the table
buffer directly. This is shown in the following illustration.
If some characteristic of the table buffer changes, such as the table name or
the table buffer location, you can simply change the reference. Your other
sanScript code will be unaffected.
• Creating references
• Assigning references
• Using references
• Guidelines
• Reference examples
Creating references
To use a reference in your application, you must first create a reference field
or variable.
Reference fields
Reference fields are like other global or local fields. The only difference is
that they use the Reference control type.
In most cases, reference fields are added to windows. Reference fields don’t
display any information, so when you add them to windows, they are
always hidden. A reference field in a window can store a reference as long
as the form it is part of remains open. A reference global variable can store a
reference as long as the application is running.
Reference variables
To create a reference local variable or parameter for a script, simply specify
reference as the variable or parameter type. A reference variable can store a
reference as long as the script it is part of is running.
Assigning references
Refer to the SanScript After you have created a place to store the reference, use the assign
Reference manual for statement to establish a reference to a specific resource. The following
more information example creates a reference to the table buffer for the Seller_Data table
about the assign attached to the current form. The reference is stored in the Main Table
statement. reference field.
You will typically assign references to resources before the resources are
first accessed. For example, a reference to a table attached to a form is often
created in the form pre script. The reference would be stored by a reference
field in one of the windows for the form.
232 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 2 5 R E F E R E N C E S
Using references
To use a reference to access a resource, you must dereference the value
contained in the reference field or variable. You do this by specifying the
type of resource contained in the reference and enclosing the reference
value in parentheses. The following example shows how to access the table
using the reference created in the previous example.
Notice that the table keyword indicates the type of resource the reference
refers to. The reference field is enclosed in parentheses.
A reference must be valid when you use it to access to a resource. If it is not, results
will be unpredictable. Dexterity doesn’t check the validity of a reference. You are
responsible for keeping track of which references are valid.
Guidelines
Keep the following guidelines in mind when using references in your
application:
• When using a reference to a table, you must refer to the keys for the
table by number. You can’t use the names of the keys, because the table
isn’t known at compile time.
Reference examples
This section describes two ways references can be used in application
development.
Example 1
This example describes how to create and use a reference to a table buffer.
Main Table is a local field using the Reference control type. It has been
added as a hidden field in the Sellers window. The Main Table field will
store a reference to a table. The SavedOnRestart property is set to true to
ensure the reference value isn’t lost when the form or window is restarted.
The following is the form pre script for the Sellers form. It creates a
reference to the Seller_Data table and stores it in the Main Table reference
field.
The following is the script for one of the browse buttons in the Sellers
window. This script is generic, meaning that it has no knowledge of what
table it is retrieving data from. It simply retrieves the first record in the table
buffer referred to by the (L) Main Table variable. Then it copies the contents
of the table buffer to the window.
{Retrieve the first record from the referenced table and display it.}
change first table('(L) Main Table');
copy from table('(L) Main Table');
234 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 2 5 R E F E R E N C E S
Example 2
This example describes how to create and use a reference to a window field.
A global reference field named Status Reference has been created for the
application and defined as a global variable.
The following is the form pre script for the Main Menu form. It creates a
reference to a status field in the Toolbar window that is part of the Main
Menu form.
Any procedures that need to display status information can use the field
indicated by the Status Reference global variable. For example, the
following statement sets the status to “Finished”.
• Overview
• User exceptions
• System exceptions
• Implementing an exception handler
• Exception handler examples
Functions from the Exception function library are used when implementing
an exception handler. Refer to them for additional information.
Overview
For Dexterity-based applications, exceptions are divided into two
categories. User exceptions are illegal or inappropriate situations that are
specific to a single application, such as a data value being outside of an
acceptable range. System exceptions are error situations that can occur in any
Dexterity-based application, such as a division-by-zero error or a form not
being found in a dictionary.
try
These statements
statements
will be executed.
catch exception
The catch clauses
handle exceptions
that are thrown. catch exception
User exceptions
User exceptions are illegal or inappropriate situations that are specific to a
single application. Exceptions are categorized according to a class and
subclass, which are long integer values that uniquely identify the exception.
When an inappropriate situation occurs in your application, you will use
the throw statement to generate an exception. When you throw an
exception, you supply the class and subclass that identify the exception that
occurred. You can also supply a message that describes the exception.
As an example, the following table lists some exceptions that can occur for
the Real Estate Sales Manager sample application.
To make your code more readable, you may want to create constants for each
exception class and subclass.
If your application integrates with Microsoft Dynamics GP, the values you use for
the class and subclass must be 22,000 or greater.
238 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 2 6 S TR U C TU R ED E X C E PT I O N H A N D L E R
System exceptions
System exceptions are error conditions that can occur in any Dexterity-
based application. Any operation that automatically displays an error
dialog to the user is considered a system exception. Common examples of
system exceptions are string overflow errors, division-by-zero errors and
database errors.
Like user exceptions, all system exceptions are identified by a class and
subclass. Both are long integer values. The classes are divided into three
categories: script exceptions, database exceptions and form exceptions. The
subclass tyically indicates the sanScript command that caused the
exception. If the sanScript command that caused the exception is not
known, another appropriate value, such as a table ID or script ID, is
provided as the subclass.
The following tables list the classes and subclasses for system exceptions.
240 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 2 6 S TR U C TU R ED E X C E PT I O N H A N D L E R
242 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 2 6 S TR U C TU R ED E X C E PT I O N H A N D L E R
The preceding table lists the most common commands and operators that
can produce exceptions. If an unlisted command has caused an exception,
you can use the exception dialog that is displayed to find out more
information about the command that caused the exception. The exception
dialog is shown in the following illustration.
Simply scroll down to the end of the message text to see the class and
subclass of the exception that was thrown.
DB_ERR_MEMORY DB_ERR_DISK_FULL
DB_ERR_NOT_INITIALIZED DB_ERR_UNKNOWN
DB_ERR_NOT_SUPPORTED DB_ERR_KEY_CHANGED
DB_ERR_TOO_MANY_OPEN_TABLES DB_ERR_NOT_VAR_FIELD
DB_ERR_INCORRECT_RECORD_LENGTH DB_ERR_CHANGED
DB_ERR_TOO_MANY_KEYS DB_ERR_DEADLOCKED
DB_ERR_TOO_MANY_SEGMENTS DB_ERR_PATH_NOT_FOUND
DB_ERR_TABLE_NOT_REGISTERED DB_ERR_BUFFER_MISMATCH
DB_ERR_TABLE_NOT_FOUND DB_ERR_COULD_NOT_CREATE
DB_ERR_LOCKED DB_ERR_INCORRECT_NUMBER_OF_KEYS
DB_ERR_BAD_FILENAME DB_ERR_SQL_TOO_MANY_CONNECTIONS
DB_ERR_TABLE_NOT_OPEN DB_ERR_SQL_DATA_ACCESS_ERR
DB_ERR_TABLE_NOT_EXCLUSIVE DB_ERR_SQL_DATA_CONV_ERR
DB_ERR_INVALID_CMD DB_ERR_SQL_GENERATION_ERR
DB_ERR_INVALID_KEY DB_ERR_NO_PRIMARY_KEY
DB_ERR_EOF DB_ERR_SQL_NOT_LOGGED_IN
DB_ERR_DUPLICATE DB_ERR_CANNOT_INIT_CTREE
DB_ERR_MISSING DB_ERR_CANNOT_INIT_BTRIEVE
DB_ERR_SET_ALREADY_ACTIVE DB_ERR_CANNOT_INIT_SQL
DB_ERR_TRANS_IN_PROGRESS DB_ERR_SQL_OBJ_NOT_FOUND
DB_ERR_NOT_VAR_LENGTH_TABLE DB_ERR_TRANS_ROLLED_BACK
DB_ERR_TABLE_NOT_DEFINED DB_ERR_SQL_CONNECTION_ERR
DB_ERR_CANNOT_LOCK_TWO_RECORDS DB_ERR_STILL_EXECUTING
DB_ERR_RECORD_NOT_LOCKED DB_ERR_SQL_CONSTRAINT_VIOLATION
DB_ERR_TABLE_DEF_MISMATCH
Location
You should implement an exception handler in places where exceptions are
likely to be generated. Common locations include:
244 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 2 6 S TR U C TU R ED E X C E PT I O N H A N D L E R
Example 1
This procedure contains sanScript code to handle the exception that can
occur when you reference an invalid form by name. If an invalid name is
supplied to the open form with name statement, a system exception is
thrown. The exception is handled by displaying a message that explains the
situation. Notice that the predefined _script_ constant is used to include the
name of the script in the message.
in string form_name;
try
{Open the named form.}
open form with name form_name;
catch [EXCEPTION_CLASS_SCRIPT_MISSING]
{Catch the exception if the form is missing.}
error "Cannot find the form " + form_name + ". Error in script " +
➥ _script_ + ".";
end try;
Example 2
This example calls a procedure that uses anonymous parameters. It uses an
exception handler to log any parameter type incompatibilities that occur
when incorrect parameters are passed to the procedure.
try
call Calculate_Payment, principle, interest, term, method;
catch [EXCEPTION_CLASS_SCRIPT_BAD_TYPE]
{Get the exception subclass.}
exception_subclass = Exception_SubClass();
exception_message = Exception_Message();
call Log_Exception, "Bad Type", exception_subclass,
➥ exception_message;
error "Invalid parameters passed to Calculate_Payment.”;
end try;
246 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 2 6 S TR U C TU R ED E X C E PT I O N H A N D L E R
Example 3
This example shows how to handle exceptions for common database
operations. The following script attempts to read and actively lock a record
from the Seller_Data table. Note that the throw system exception for table
statement is used to throw a system exception if a database error occurred.
If the record can’t be actively locked, the script will retry 10 times before
displaying a message to the user. The restart try statement causes the
statements in the try...end try block to be executed again, but the values of
local variables are not affected.
try
'Seller ID' of table Seller_Data = 'Seller ID' of window Sellers;
{Attempt to actively lock the record.}
change table Seller_Data, lock;
increment read_count;
{Check the error. If an error occurred, an exception will be
thrown.}
throw system exception for table Seller_Data;
catch [EXCEPTION_CLASS_DB]
{A standard database error occurred.}
if err() = LOCKED then
{Retry the read operation 10 times.}
if read_count < 10 then
restart try;
else
error "Unable to read and actively lock the record.";
end if;
end if;
else
{Some other exception occurred.}
error "An unknown exception occurred. Class: " +
➥ str(Exception_Class()) + " Subclass: " +
➥ str(Exception_SubClass());
end try;
Example 4
This example is a data verification procedure that uses throw statements to
throw user exceptions when invalid data values are encountered. Constants
have been added to the dictionary for each class and subclass of exception
that can be thrown.
in table Buyer_Data;
try
{Read the current record.}
change table Buyer_Data;
while err() = OKAY do
call Verify_Buyer_Record, table Buyer_Data;
change next table Buyer_Data;
end while;
248 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 2 6 S TR U C TU R ED E X C E PT I O N H A N D L E R
catch [BUYER_DATA]
{Get the exception subclass}
exception_subclass = Exception_SubClass();
case exception_subclass
in [NO_NAME]
'Buyer Name' of table Buyer_Data = "Unknown";
in [NO_AGENT]
'Agent' of table Buyer_Data = "Staff";
in [INVALID_PRICE]
'Asking Price' of table Buyer_Data = 0;
end case;
{Save the repaired record.}
save table Buyer_Data;
{Check the record again}
restart try;
else
{Some other exception occurred.}
error "An unknown exception occurred. Class: " +
➥ str(Exception_Class()) + " Subclass: " +
➥ str(Exception_SubClass());
end try;
• Chapter 28, “Script Profiler,” explains how to use the Script Profiler to
analyze application performance.
• Chapter 29, “Script Logger,” explains how to log the scripts that are
executed as a Dexterity application is running.
252 P R O G R A M M E R ’ S G U I D E V O L U M E 2
Chapter 27: Script Debugger
Dexterity has a source-level script debugger that you can use to debug
scripts and analyze your application. Using the debugger, you can set
breakpoints in any script, apply conditions to breakpoints, step through
scripts a line at a time, and examine and set the values of variables and
fields in the application.
Calls
This menu item opens the Calls window, which displays a list of the scripts
currently being run.
Breakpoints
This menu item opens the Breakpoints window, which lists the breakpoints
that have been set in the application. This window also allows setting
options for breakpoints.
Expressions
This menu item opens the Expressions window. This window allows you to
view and alter the contents of variables and fields.
Locals
This menu item opens the Locals window, which displays the values of the
variables and fields referenced in the current script.
Table Buffers
This menu item opens the Table Buffers window. The Table Buffers window
allows you to view the contents of the table buffers for the tables in the
application.
To open this window, choose Script Debugger from the Debug menu. The
Script Debugger window also appears when a breakpoint is encountered
while an application is running.
254 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 2 7 S C R I PT D E B U G G E R
Debugger controls
The operations The buttons at the top of the Script Debugger window allow you to select
performed by these scripts, insert and remove breakpoints, and control the script debugger
controls are described when a breakpoint is encountered. Each button is described in the
in more detail later in following table.
this chapter.
Button Name Description
Open Script Opens the Open Script window, allowing you to select a
script to debug.
Edit Script Opens the Script Editor, allowing you to edit the script
currently displayed in the Script Debugger window.
Context menu
The Script Debugger window has a context menu that is available in test
mode. You can use the context menu to copy the selected text, add a watch
based on the selected text, toggle a breakpoint on the current line, or set the
next statement to execute.
Opening scripts
To open a script to debug, click the Open Script button in the Script
Debugger window. The Open Script window will appear, as shown in the
following illustration.
256 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 2 7 S C R I PT D E B U G G E R
Breakpoints
Editing scripts A breakpoint is a marker you can place on a line in a script. When a
containing breakpoints breakpoint is encountered while the application is running, the application
can affect the will stop just prior to executing the line of code on which the breakpoint
breakpoints. Refer to was placed. When stopped at a breakpoint, you can examine the state of the
the section Editing application.
scripts on page 270 for
more information. Setting breakpoints
Once you’ve selected a script and it appears in the Script Debugger
window, you can set breakpoints. To set a breakpoint, place the insertion
point in the line where you want to set the breakpoint, then click the Toggle
Breakpoint button. A red dot indicates a breakpoint is set. To remove a
breakpoint, place the insertion point in the line containing the breakpoint
and click the Toggle Breakpoint button again.
You can’t set breakpoints on comment lines. The Script Debugger will set a
breakpoint on the next available line if you try to do so.
Breakpoints can be set in either tools mode or test mode. Moving between
tools mode and test mode doesn’t affect breakpoints. The breakpoints you
set will remain in the scripts until you specifically remove the breakpoints
or quit Dexterity.
Breakpoints window
To view a list of the breakpoints you’ve set, choose Breakpoints from the
Debug menu. The Breakpoints window will appear, as shown in the
following illustration.
You can enable, disable or delete a breakpoint by selecting it in the list and
clicking the appropriate button. The enabled status for a breakpoint is
indicated by the + or - in the second column of the breakpoints list (+ is
enabled, - is disabled). If the Ignore All Breakpoints check box is marked,
causing all breakpoints to be ignored, all enabled breakpoints will be
indicated with a small “e”, while disabled breakpoints will be indicated
with a small “d”.
258 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 2 7 S C R I PT D E B U G G E R
Breakpoint options
You can set additional options to specify when a breakpoint should halt
execution. To do so, select a breakpoint in the list and click Options. The
Breakpoint Options window will appear, as shown in the following
illustration.
Condition Description
Break When Expression is Breakpoint is honored when the boolean sanScript
True expression entered in the field below the option is true.
Break After Nth Breakpoint is ignored until it has been encountered the
Occurrence number of times indicated. After this number of
encounters, the breakpoint is honored like a normal
breakpoint.
Break For N Occurrences Breakpoint is honored the until it has been encountered
the number of times indicated. After this number of
encounters, the breakpoint is ignored.
Encountering a breakpoint
When a breakpoint is encountered while the application is running, the
Script Debugger window will open. The script containing the breakpoint
will be displayed, and a yellow arrow will indicate where execution
stopped. An example of this is shown in the following illustration.
When a breakpoint is
encountered, the script
containing the breakpoint
is displayed in the Script
Debugger window.
The yellow
arrow indicates
where execution
is currently
stopped.
Controlling execution
While the application is stopped at a breakpoint, all application operations
are suspended. You must use the Go or Step menu commands in the Debug
menu (or their corresponding buttons in the Script Debugger window) to
continue executing the application.
260 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 2 7 S C R I PT D E B U G G E R
Go
Choosing Go from the Debug menu or clicking the Go button in the Script
Debugger window causes the application to continue execution until
another breakpoint is encountered or all scripts finish.
You can’t exit test mode while stopped at a breakpoint. To exit test mode, the scripts
currently running must finish executing. Mark the Ignore All Breakpoints option
in the Breakpoints window and then click Go to allow all scripts to finish executing
without stopping at any breakpoints. Then you can exit test mode.
Step
Choosing Step from the Debug menu or clicking the Step button in the
Script Debugger window causes the next line of the current script to be
executed. The application is then suspended as if another breakpoint had
been encountered. This is useful when you want to step through an
application line-by-line to see how individual statements affect the
application.
Step In
Step In is used when the application is stopped on a line that calls a
procedure or user-defined function, or uses the run script statement.
Choosing Step In from the Debug menu or clicking the Step In button
causes the called script to appear in the Script Debugger window. Execution
stops on the first line of that script.
Step Out
Step Out causes the current script to finish executing, then the script that
called the current script is displayed in the Script Debugger window.
Execution stops in the calling script on the line immediately following the
line that caused the called script to be run.
Step In
Exercise caution when using this feature. It will alter the order that statements are
executed in the application and may cause different results from when the
application runs normally.
262 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 2 7 S C R I PT D E B U G G E R
Calls window
To display the Calls window, choose Calls from the Debug menu. This
window is shown in the following illustration.
For more information This window displays the scripts in the current call stack and the line
about call stacks, refer number at which each script is stopped. The call stack indicates the
to Chapter 24, “Call sequence of scripts called in the application. You can debug the script
Stacks.” selected in the Call Stack by clicking Open; you can edit the script by
clicking Edit.
The Calls window contains information only when the application is stopped at a
breakpoint.
Clicking Unwind continues executing the script selected in the call stack
and terminates all scripts that appear above the selected script in the call
stack. (The scripts are terminated as though an abort script statement had
been executed.) Unwind can be useful if you know a process isn’t working
properly and there is little point in allowing the process to continue.
Exercise caution when using the Unwind button. It will terminate scripts without
allowing them to finish normally. This could cause problems in your application,
such as if a posting routine isn’t completed.
Locals window
The Locals window displays information about the variables, fields, and
parameters referenced by the script currently selected in the Calls window.
The Locals window is accessed by choosing Locals from the Debug menu,
and is shown in the following illustration.
When you select a different script from the call stack in the Calls window,
the contents of the Locals window updates automatically.
The Locals window contains information only when the application is stopped at a
breakpoint.
264 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 2 7 S C R I PT D E B U G G E R
Expressions window
The Expressions window is accessed by choosing Expressions from the
Debug menu. The window is shown in the following illustration.
This window allows you to view the value of any valid sanScript
expression. Simply type the expression in the Expression field and click
Evaluate. If the expression can be evaluated, its value will be displayed in
the Value field. Otherwise, the Value field will contain a message indicating
the expression couldn’t be evaluated.
You can also use the Expressions window to set the value of fields or
variables in the application. Enter a sanScript expression indicating the item
whose value you want to set, enter the value in the Value field, then click
Set. Dexterity will attempt to set the item referenced by the expression to
the value you entered. If the item can’t be found or the value isn’t valid, the
Expressions window will beep.
Watch window
The Watch window is accessed by choosing Watch from the Debug menu.
The window is shown in the following illustration.
Similar to the Expressions window, the Watch window allows you to view
the values of multiple valid sanScript expressions. Simply type the
expression in the Expression column. If the expression can be evaluated, its
value will be displayed in the Value column. Otherwise, the Value column
will contain a message indicating the expression couldn’t be evaluated.
Another way to add a watch expression is to select text in the Script Debugger
window, right-click the text, and then choose Add Watch from the context menu.
The expressions in the Watch window are saved in a c-tree table in the same
location as the dictionary being edited. This means you don’t have to re-
enter them each time you are debugging a dictionary. To remove an
expression from the Watch window, place the focus in the row and click
Remove. To remove all expressions, click Remove All.
The values of the expressions are updated when you click Refresh in the
Watch window. They are also updated in the following cases:
266 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 2 7 S C R I PT D E B U G G E R
If you aren’t certain of the status of the expressions in the Watch window, click
Refresh to ensure the expressions are up-to-date.
If you fully-qualify the expressions in the Watch window, you can avoid any issues
with context.
Some values returned from expressions in the Watch window are too
complex to display in a single line. Composite fields with multiple
segments, and text fields with multiple lines of text are a common
examples. To view these complex return values, you can use the Visualizer.
Place the focus in the line in the Watch window containing the value you
want to view, and then click Open Visualizer. The Visualizer will show the
content of the expression. For instance, the following illustration shows the
list_object composite used for a card list in Microsoft Dynamics GP.
You can view table buffers for forms, procedures and functions.
• To view the contents of a table buffer for a form, select the dictionary
and form the table buffer is associated with. Then select the table in the
Table list.
268 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 2 7 S C R I PT D E B U G G E R
Dictionary
This is a list of products that are currently available in the application.
Form
This is a list of forms that are currently open in the application. You can
view the table buffers that are part of each form. If a form is open but
doesn’t appear in the list, click the Fill Forms button to update the list.
Table
This is a list of the tables that are attached to the currently-selected form.
Select a table in this list to view the contents of its table buffer. If the tables
for a form don’t appear in this list, click the Fill Table Buffers button to
update the list of tables.
Fields
Once you’ve selected the table buffer to view, the Fields list displays the
fields and their values. The values displayed are updated automatically
when a breakpoint is encountered. You can also update the values by
clicking the Update Fields button.
Key
The keys for the selected table are displayed in the Key list. These are for
informational purposes only. They don’t indicate which key was used to
access the record in the table buffer.
Table information
The Info list contains additional information about how the table is being
accessed. The following table describes the items that appear in this list.
Item Description
Pathname The path that was used to access the table.
Use Count The number of table buffers the current client has for the
table.
Open Count The number of instances of the table being open on the
current client.
Record Length The length of the fixed-length portion of the record (in bytes).
Max Variable Len The maximum length of the variable-length portion of the
record (in bytes).
Number of Fields The number of fixed-length fields in the table definition.
Number of Variable The number of variable-length fields (pictures and text fields)
Fields in the table definition.
Number of Keys The number of keys defined for the table.
Item Description
Dictionary ID The ID of the dictionary the table definition is part of.
IsTemp A boolean indicating whether the table is a temporary table.
OS Name The physical name for the table (operating system name or
SQL table name).
Active Lock Count The number of active locks the current client has on the table.
Default File Handler The default database type to be used for the table.
Open A boolean indicating whether the table is open.
Open Mode A string indicating whether the table is opened with read-only
access, for shared access or for exclusive access.
Edit non-existing A boolean that is set to true when the edit table statement
record has reserved a record in the table. False indicates no record
was reserved.
FM Error A string indicating the error that occurred on the last table
operation.
Key Range An integer indicating the number of the key used to access
the current range of records. If a range isn’t being accessed,
this will be None.
Active Lock A boolean that is set to true when the record is actively
locked. Otherwise, it is set to false.
Editing scripts
You can edit the script displayed in the Script Debugger window by
clicking the Edit Script button. The script editor will open, allowing you to
edit the script.
270 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 2 7 S C R I PT D E B U G G E R
When an error halts a script in test mode, the alert message dialog box will
contain two buttons. An example is shown in the following illustration. In
this example, a division by zero error was encountered during script
execution.
Click OK to return to test mode. Click Debug to see the name of the script
that encountered the error. An example of the window that displays the
name of halted scripts is shown in the following illustration.
The set of scripts that were being processed at the time the application
stopped will be displayed in the window. The topmost script is the first one
that was called. If it called a second script, the second script will be shown
on the next line, and so on. The script at the end of the list was being run
when the error was encountered. Values shown after a script name are the
parameters that were passed to the script.
Click the Where button to open the Script Editor. The script that halted will
be opened, and the insertion point will appear on the line where execution
halted. You may edit the script normally at this point. Depending upon the
error encountered, it may be necessary to restart your application in test
mode before testing it again.
As with any debugging tool, the Dexterity runtime debugging feature can take you
only to the point where it was no longer able to continue script processing. The
actual cause of an error, such as a variable that was assigned an invalid value,
might be in a different location.
272 P R O G R A M M E R ’ S G U I D E V O L U M E 2
Chapter 28: Script Profiler
The Script Profiler lets you analyze script and table activity during test
mode, allowing you to optimize your scripts and streamline your
application. Once you’ve started the profiler, it records the name of every
script run and how much time is spent running each script. The Script
Profiler also tracks how many times tables were accessed and the average
and total times for specific operations on each table accessed. The time
required by the profiler itself is automatically deducted from the profile, so
that the profile shows the time requirements for only your application.
Information about the Script Profiler is divided into the following sections:
Profile Scripts
Choose this menu item to start the process of profiling scripts. When you
choose it, Dexterity will begin keeping a record in memory of all the scripts
called by your application and the tables that were accessed. When you
save the profile, it will be copied from memory to a disk file.
You can turn the script profiler off at any time by choosing the Profile
Scripts menu item. If you turn the profiler on again later, the new profile
information will be added to the previous information. This allows you to
selectively control which portions of the application are profiled. For
instance, if you want to profile only the scripts from an invoicing window
and an inventory window, you can turn the script profiler on while the
invoicing window is active, turn it off while using other windows, and turn
it on again when the inventory window is active.
Be sure to turn off the script profiler before leaving test mode. Otherwise, it will
profile scripts called by Dexterity, making it difficult to tell your application’s
scripts from Dexterity’s.
Clear Profile
Choose this menu item to clear current profile information from memory
and start a new profile.
Save Profile
Choose this menu item to save the current profile to a disk file. Choosing
this menu item doesn’t clear the current profile from memory or turn off the
script profiler; it simply saves the current profile to the location you specify
so you can examine its contents. When you choose this item, a dialog box
will appear; specify a name, location and file format for the profile. You can
select text format or tab-delimited format.
• Text files are stored as text that can be read by a text editor.
The following is an example script profile that has been saved as a text file.
Total Time for All Tables 0.06 0.02 0.01 0.00 0.03 0.00 0.00 0.00 0.00 0.12
274 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 2 8 S C R IP T P R O FI LE R
Script information
The script information section describes the performance of scripts in your
application. Each line in the section describes a script that was accessed in
your application while the Script Profiler was on.
Count This column displays the number of times the script was accessed
while the profiler was on.
Time in This column displays the amount of time spent running the
script.
+Children This column displays the amount of time spent running the
script plus the amount of time spent in any procedures or functions called
by the script. The scripts are sorted according the values in this column.
The time values shown in the profile are derived from the operating system. The
actual time represented by these numbers will vary between operating
environments, and is affected by a number of system settings. The numbers are best
viewed as relative indicators of the amount of time spent in each script.
Database Information
This section displays information about tables that were accessed while the
profiler was on. Three categories are listed:
Average time per operation This category lists the average time
required to perform each specific table operation for each table. The times
are listed in milliseconds.
Total time per operation This category lists the total time required to
perform each specific table operation for each table. It also lists the total
time spent for operations on each table and the total time for all tables. The
times are listed in seconds.
Table Name These columns display the name of each table for which
information was logged.
Read Next These columns display the number of times a get next or
change next statement was used to read a record from each table, and the
average and total times for these operations.
Read First These columns display the number of times a get first or
change first statement was used to read a record from each table, and the
average and total times for these operations.
Read Other These columns display the number of times any other type
of command read a record from each table, and the average and total times
for these operations. Commands such as get previous, get last and
countrecords() are logged in this column.
Insert These columns display the number of times a new record was
inserted into each table using the save table statement, and the average and
total times for these operations.
276 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 2 8 S C R IP T P R O FI LE R
Range Sum These columns display the number of times a sum range
statement was used to sum the numeric fields for each table, and the
average and total time for these operations.
Script cache
The last section of the script profile describes the performance of the script
cache. It lists the number of procedures, form-level procedures, functions,
and form-level functions that were executed. It also lists the number of
“hits” or times the procedure or function was read from the cache. The hit
rate percentage is also calculated. This is the ratio of the number of hits and
the total number of calls.
The script cache is cleared only when you recompile a script in the application. This
means that if a script was run in a previous test mode session, it may not have to be
reloaded when it is executed. This will distort the cache hit rate to appear higher
than it actually is. If you are doing benchmarking or performance tuning, we
recommend that you quit Dexterity and restart the application so you see accurate
values for the cache.
Logging scripts
To use the script logger, start your application in test mode. Choose Log
Scripts from the Debug menu. A dialog box will appear, allowing you to
enter a name and location for the script log. The log file created is a text file
that can be viewed with a text editor. The Script Logger will log all scripts
until it’s turned off.
You can turn on the script logger before entering test mode if you want to log the
Main Menu form pre script and any startup scripts. Be sure you turn off the script
logger before leaving test mode. Otherwise, it will log scripts called by Dexterity,
making it difficult to tell your application’s scripts from Dexterity’s.
The scripts in the log file are listed in the order in which they were
executed. Scripts that appear indented were called by or ran as a result of
the script they are indented beneath. Any values passed to procedures or
functions follow the script name in the log.
You can also use the Script Logger when optimizing the performance of
your application. For example, if some operation seems to be particularly
slow, you can use the Script Logger to list all scripts that were run to
perform the operation. You then have a list of the scripts to target for
optimization.
If your application integrates with Microsoft Dynamics GP, you can use the
Script Logger to list which scripts are run when a particular operation
occurs in the accounting system, such as posting. Simply run Microsoft
Dynamics GP in test mode and start the Script Logger before performing
the operation in question. The script log allows you to see what is
happening in the accounting system without having to look at source code.
280 P R O G R A M M E R ’ S G U I D E V O L U M E 2
Chapter 30: Resource Usage
When developing an application, it’s often useful to know how resources
have been used. The following is a description of how to use the Reference
Information window to find out how resources and scripts are used in your
application. Information is divided into the following sections:
Mark the items for which you want to view reference information. For
example, if you want to find out how tables are used in your application,
mark Tables. To see how scripts refer to the various resources in your
application, mark Scripts. Then click Build Table to build the reference
information table.
For large dictionaries like Dynamics.dic, building the reference information table
for all resource types can take several minutes.
You can choose the following items from the References button:
282 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 3 0 R E S O U R C E U S A G E
The tree view on the left side of the window is filled based on the choice
you made from the References button. If you choose Refers To, the tree view
lists the resources and scripts this particular resource refers to.
If you choose Referenced By, the tree view lists the resources and scripts
that refer to the this resource.
The tree view is expandable, allowing you to drill down to view the hierarchy of
resources used. You can use the Print button in the status area to print the contents
of the Reference Information window.
When you select an item in the tree view, the list on the right side of the
window displays a detail view of all the items in the dictionary that refer to
the item selected. In the following illustration, the data type STR30 is
selected in the tree view. The list on the right side of the window shows a
detail view of all the other resources that reference the STR30 data type.
If your dictionary contains script source, you can double-click the name of any
script to automatically open it in the script editor.
To update reference information, open the Options window and display the
Reference tab. Mark only the items you want to update. For example, if you
have been changing scripts, but other resources are unchanged, mark only
the Scripts item. Click Build Table to update the reference information table.
If you aren’t sure how up-to-date the reference information table is, you
may want to click Clear Table to remove all information from the table.
Then mark the appropriate items and click Build Table to re-create the table.
284 P R O G R A M M E R ’ S G U I D E V O L U M E 2
PART 7: SOFTWARE ENGINEERING
Part 7: Software Engineering
Use the information in this portion of the documentation to learn about the
process of creating stand-alone applications with Dexterity and managing
the development process. Following is a brief explanation of the topics
discussed:
286 P R O G R A M M E R ’ S G U I D E V O L U M E 2
Chapter 31: The Development Process
To develop high-quality applications, a standard development process
should be followed. This portion of the documentation describes the steps
of a standard software development process. The information is divided
into the following sections:
• Application development
• Dexterity application development
• Managing development
Application development
Several different methods can be used when developing applications.
Though a complete discussion of development methods is beyond the
scope of this documentation, most development methods share the
following four major steps:
288 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 3 1 TH E D EV EL O P M E N T PR O C E S S
To use the runtime engine to run your application, you’ll enter product
information for the application dictionary and create a launch file to
start the application. Adding product information is described in
Chapter 57 of Volume 1 of the Dexterity Programmer’s Guide. Using a
launch file to start an application with the runtime engine is described
in Chapter 58 of Volume 1 of the Dexterity Programmer’s Guide.
Managing development
How you manage application development depends on how large the
application is you will be creating, and the number of developers you will
have working on the project.
• Resources
• Scripting
• Features
Resources
The following guidelines apply to various resources in your application.
Data types
If a field has a unique purpose, create a separate data type for it. This allows
the characteristics of the field to be easily modified without affecting other
fields in the application.
Formats
Provide common formats in your application so Report Writer and
Modifier users won’t have to create them.
Fields
When possible, use global fields only for data stored in tables. For fields
that appear only on windows, use local fields. Local fields are stored with
the form. This helps encapsulate a form, making it more portable and easier
to transfer to another Dexterity application.
Tables
Consider supplying extra fields in your tables. Extra string, long integer
and currency fields in the table auto-linked to a window allow Modifier
users to add fields to the window. Be sure you use the copy from and copy
to statements to move data between the window and the table buffer. This
ensures that the data in the extra fields will automatically be saved in and
retrieved from the table.
Forms
Design your forms to be “portable.” Portable forms can be easily
transferred into another and work with little or no modification. Portable
forms allow you to easily re-use your form in another dictionary. To
make forms portable, follow these guidelines:
Reports
Consider supplying additional data through report legends, even if you
don’t use them in the reports you provide with your application. Report
Writer users may find the values supplied in legends useful on reports. Be
sure to document any legends that you supply for reports.
Pictures
When possible, use pictures instead of native pictures. Pictures are platform
independent, so you don’t need to create separate pictures for each
platform.
Global variables
Minimize the use of global variables in your application. Because of their
global nature, system variables are prone to causing application errors.
Errors can occur when one portion of an application changes the value of a
system variable without taking into account the effects on other portions of
the application.
292 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 3 2 D E S I G N G U I D EL I N E S
Scripting
Keep the following guidelines in mind when adding scripts to your
application.
Procedures
Use procedures to perform process-intensive tasks. Doing so allows these
tasks to be easily sent to a process server for remote processing. Refer to
Background and remote processing in Chapter 20, “Procedures,” of Volume 2 of
the Dexterity Programmer’s Guide for more information.
Scripts
If your application contains code to validate data such as account numbers
or customer IDs, move this code to form-level procedures or form-level
functions. Don’t put validation code in field scripts. By placing the
validation code in a form-level procedure or function, you make it available
to other parts of the application that may need to perform the same
validation.
Consolidate common operations like saving and deleting records into form-
level procedures. Often, these operations are performed in several locations
in the form. By consolidating these operations, you reduce code duplication
and maintenance.
Features
Consider implementing the following features in your application.
Customization
Make your application customizeable. The most common customization is
support for system colors and custom colors. You could also provide access
to the Modifier and Report Writer.
You may choose to use defaults file settings to store customization options.
Any customizations you make available should be accessed through an
options window. Don’t make users edit the defaults file directly. Be sure to
document any defaults file settings you use. Don’t make defaults file
settings mandatory. Your application should be able to run properly,
regardless of defaults file settings.
Your application should have some means of exporting data. Typically, data
is exported to text files. You could write scripts that export data to text files,
or you could create reports that export the data to text files. The ability to
export data allows users to work with your application’s data in other
applications.
Setup information
Your application should provide information about the current setup.
Typically, this information is provided in the application’s About Box and
includes the following:
• Application version
• Dexterity version
• ODBC driver version (if applicable)
• Platform
• Operating system
• Special features enabled
294 P R O G R A M M E R ’ S G U I D E V O L U M E 2
Chapter 33: Optimizing Your Application
This portion of the documentation contains tips that can help you improve
the performance of applications you create with Dexterity. Information
about optimizing is divided into the following sections:
Refer to Chapter 28, Typically, the script cache has about a 97% hit rate. Use the Script Profiler to
“Script Profiler,” for gauge how well the script cache is performing. The cache information is
information about located at the end of the script profile.
using the Script
Profiler. For most applications, the default script cache will be adequate. However, if
your application performs operations that use a combination of more than
50 procedures and user-defined functions, you may want to increase the
size of the script cache. If the cache hit rate is lower than average, increasing
the script cache size could improve performance.
Keep in mind that setting the cache size larger will require more memory.
At a minimum, Dexterity will cache 35 scripts. If Dexterity is running out of
memory and can’t perform actions such as opening forms, the script cache
will automatically be flushed to make more memory available.
Table buffers
Opening several tables in a procedure or function can affect application
performance. The first table a procedure or function opens takes the most
time to open. Subsequent tables opened by the procedure or function take
less time to open.
Window updating
If possible, use the copy from table, copy to table, copy from table to
window, and copy from window to table commands to update window
fields, instead of using the set statement. These commands provide better
performance when updating windows and result in shorter scripts.
296 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 3 3 O P T I M I Z I N G Y O U R A P PL I C A T I O N
Tables
Table access
For data-intensive applications, application speed is mainly based upon the
number of records being read from and written to tables. The more records
that are read and written, the slower the application. To improve the speed
of an application, minimize the number of records that are read from and
written to tables.
Clearing tables
If you are removing all records from a table, consider deleting and then
recreating the table. For tables containing many records, this may be faster
than removing the records individually or setting up a range for the entire
table and removing the range.
Keys
The time required to write records to a table is directly proportional to the
number of keys defined for the table. To improve performance, reduce
the number of keys in each table.
The total length of the segments of a key also affects performance. The
longer the segments for the key, the larger the table index becomes. Larger
table indexes take longer to search, increasing the time to read and write
records. To improve table performance, reduce the number of key segments
and the length of the segments for each key.
Ranges
If you are working with groups of records in a table, use ranges when
possible. Some database managers, such as SQL database managers,
perform operations on ranges of records very efficiently. For example, SQL
database managers can remove a range of records much faster than they
can remove the records individually.
Table size
As tables become larger, read and write times become longer. To improve
table performance, keep the size of tables as small as possible.
Temporary tables
Refer to Temporary In some cases, you may be able to speed up network operations by using
tables in Chapter 9, temporary tables. For example, if you’re doing considerable processing on a
“Tables,” of Volume 1 range of records in a table stored on a network drive, you may want to copy
of the Dexterity those records to a temporary table stored on your local drive, perform the
Programmer’s Guide necessary processing using the temporary table, then copy the records back
for information about to the table on the network. This can greatly reduce network traffic and
creating temporary improve performance.
tables.
When creating temporary tables, create only one key for the table and keep
the length of the key to a minimum. This will improve the performance
when reading and writing records for the temporary table.
Reports
Use text reports when you want maximum performance printing reports.
Text reports are simpler than graphics reports, so they print much faster.
Dexterity has also been optimized for maximum performance when
printing text reports.
Scrolling windows
Fill scripts are typically used to read records from tables other than the table
linked to the scrolling window. Because this increases the time required to
fill the scrolling window, avoid using fill scripts for scrolling windows
whenever possible. Instead, consider adding the fields being read by the fill
script to the table linked to the scrolling window.
Also, try to avoid using the reject record statement in the scrolling window
fill script because many additional records may have to be read and rejected
before the scrolling window is filled. If several records shouldn’t be
displayed in a scrolling window, either set a range on the linked table or
consider using a temporary table.
Scripts
Refer to Chapter 28, Use the Script Profiler to find out which areas of your application are
“Script Profiler,” for potential candidates for optimization. The scripts are listed in order in the
information about script profile, with the scripts at the top of the list taking the most time to
using the Script execute. These are the scripts you should target for optimization.
Profiler.
298 P R O G R A M M E R ’ S G U I D E V O L U M E 2
Chapter 34: Testing Your Application
This portion of the documentation describes the characteristics of a good
application, introduces the process of software quality assurance, and
describes special considerations when testing Dexterity applications. It also
describes Dexterity’s built-in macro system, which can be used to automate
testing. Information is divided into the following sections:
If you want to have a high-quality application, you can’t just test it when
you’ve finished coding. The process of creating a quality application starts
at the design stage and continues throughout the development cycle. This
process is typically referred to as Software Quality Assurance (SQA).
After the application has been designed, the coding process can begin.
During the coding process, these guidelines should be followed:
300 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 3 4 T E S T I N G Y O U R A P PL I C A T I O N
Many of the test procedures developed to test the software can be recorded
as macros. Macros allow exactly the same test procedures to be repeated,
without introducing the factor of human error. This allows for regression
testing, or retesting the application. After changes have been made to the
application, regression testing is performed to ensure that existing
functionality in the application hasn’t been affected.
Database types
You should test all of the different database types your application will
support. Refer to Appendix A, “Dexterity Limits,” in this manual for a list
of the limits for the various database types.
Network operation
If your application will be operating in network environments, you should
run your tests for each environment you will be supporting.
Multiple users
If multiuser operation is supported by your application, be sure to test your
application with multiple users.
Video configurations
Check the sizes of your application’s windows for the different video
configurations you will be supporting. Be sure you haven’t made windows
too large to appear normally when using lower-resolution video displays.
Also, be sure to check how your application looks on a monochrome or
gray-scale display.
Tool access
If you are licensing and providing access to tools such as the Report Writer
or the Modifier, be sure they can be accessed properly from within your
application.
Windows DLLs
If you have written any DLLs to be used with your Dexterity application, be
sure these are thoroughly tested.
Online help
If your application has online help, be sure you’ve tested it thoroughly to
ensure it is operating properly and that correct information is displayed.
Printed documentation
Verify that descriptions and procedures described in the documentation
match the behavior of the software.
Phases of testing
The first phase of testing is finished when your application performs
according to its requirements, has been thoroughly exercised, and all test
cases you’ve written indicate the application is working properly.
The next phase is beta testing, when you release your application to a select
group of customers. The beta testing phase allows your customers time to
thoroughly work with your application and report any problems or
suggestions. It also allows you an opportunity to fix reported problems and
make necessary changes to improve your application.
When the beta testing phase is complete, you’re ready to send your product
to your customers. Depending upon how comfortable you are with the
quality of your application, you may want to do a controlled release
(releasing to a limited number of customers) before you make your
application generally available.
302 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 3 4 T E S T I N G Y O U R A P PL I C A T I O N
Pause/Continue
This menu item allows you to pause a macro that is currently playing.
Choose Continue to resume playing the macro.
Step
When a macro is paused, selecting this item causes the next statement in the
macro file to be run.
Insert Pause
When a macro is being recorded, selecting this item allows you to insert a
pause in the macro. A window will appear, prompting you to enter the
string that will be displayed when the pause is encountered during
playback.
When the macro is played, a dialog box containing the message will be
displayed, as shown in the following illustration.
Close the dialog box and choose Continue from the Macro menu to allow
the macro to continue running.
Some additional menu items are available to help you use the macro system
to test your application. To access these additional items in test mode or
with the runtime engine, add the ShowAdvancedMacroMenu=TRUE
setting to the defaults file. To access these additional items from within
Dexterity, hold down the SHIFT key and choose Play from the Macro menu.
Click Cancel in the dialog box that appears. The next time you display this
menu, five additional items will have been added to it. If you quit and
restart your application, the Macro menu will return to its original size.
304 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 3 4 T E S T I N G Y O U R A P PL I C A T I O N
We strongly recommend that you don’t tell your customers about the additional
items in the Macro menu. These items are for developer use only.
Status
This menu item opens the Macro Status window.
When a macro is being recorded, the Macro Status window will list the
name of the macro and how many lines it currently has.
Refer to the When a macro is being played, the Macro Status window lists the name of
StdInternationalInfo the macro and the line currently being executed. The
setting in the Dexterity StandardInternationalInfo option will be checked when standard
online help to learn international information is being used while the macro is running. The
about standard status of this check box is determined by the StdInternationalInfo setting
international in the DEX.INI file and can’t be changed by the user.
information.
The No User Interface Flash option will prevent the momentary pause that
normally occurs to make push buttons appear as if they have been pressed.
Removing this pause will allow macros to be executed more quickly.
The Macro Status window also allows you to begin executing a macro at a
specific line or insert a pause after a specified line. To start a macro at
a specific line, enter the line number in the Jump to Line field and click OK,
or double-click the line in the scrolling window. To pause after a specific
line, enter the line number in the Pause After Line field.
The scrolling window at the bottom of the window displays the lines in the
macro. The scrolling window is useful when you want to start a macro at a
specific line or when you’re trying to locate an error in a macro.
Because macros can be lengthy, by default only the first ten lines of the
macro are displayed. To allow more than ten lines to be displayed, enter the
number of the last line you want to display in the Lines to Display field and
press the TAB key. The scrolling window will then be able to display from
the first line in the macro to the line you specified. To begin displaying a
macro from a specific line, enter the line number in the Jump To Line field
and press the TAB key.
For example, to display line 435 of a 2000-line macro, enter 500 in the Lines
to Display field and press the TAB key to allow the first 500 lines of the
macro to be displayed. Enter 435 in the Jump to Line field and press the TAB
key. After a moment, the scrolling window will begin displaying the macro
at line 435.
306 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 3 4 T E S T I N G Y O U R A P PL I C A T I O N
Window Dump
Choosing this item while recording a macro inserts a command to save an
image of an application’s currently active window as a graphics file. When
the macro is run on different versions of the application, the images from
each version can be compared to ascertain whether the results are the same.
The Window Dump menu item opens a dialog box allowing you to name
the graphics file to which the image of the current window will be saved. It
also inserts a command into the macro to save the image when the macro is
played. The image will be saved as a PC Paintbrush (.PCX) file.
The Field Dump menu item opens a dialog box allowing you to name the
text file to which the names and contents of fields in the current window
will be saved. It also inserts a command into the macro to save the fields’
contents.
Field dumps must not be taken when a change is made to a field and the focus
remains on it. The field dump will not reflect the change made to the field until the
focus leaves the field.
Menu Dump
Choosing this item while recording a macro inserts a command to save the
names and contents of all form-level menus that are attached to the
currently-active form to a text file. A dialog box will appear, allowing you
to name the text file in which the menu information will be saved. When the
macro is run on different versions of the application, the text from each
version can be compared to determine whether the results are the same.
Background Dump
Choosing this item while recording a macro inserts a command to save the
background HTML document to a text file. A dialog box will appear,
allowing you to name the text file in which the information will be saved.
This is used when testing the “Home Page” functionality in an application.
Shell Dump
Choosing this item while recording a macro inserts a command to save the
contents of the MDI shell (main runtime window) to an XML file. A dialog
box will appear, allowing you to name the text file. This can be useful when
testing the navigation in an integrating application.
Table Dump
Choosing this menu item opens the Table Export window. This window
allows you to export the contents of a table to a text file. During testing, the
contents of the text file can be compared with previous versions to find out
if any differences have occurred.
Open Form
Choosing this item opens the Macro Open Form window. This window
allows you to open a specific form without having to record the steps
necessary to open it using the application’s interface. This allows you to
record macros that are independent of the navigation in the application.
Insert Header
This menu item opens a window allowing you to specify information about
the macro being recorded. This information can include a description of the
macro, a list of the windows that must be open, or any other preconditions
necessary for the macro to run properly. The information you enter in this
window is saved in comment lines at the beginning of the macro file.
308 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 3 4 T E S T I N G Y O U R A P PL I C A T I O N
Insert Comment
This menu item opens a window allowing you enter comments for the
macro being recorded. Each time you add a comment, it is saved as a
comment in the next line of the macro file.
For example, it’s not efficient for a macro to open a lookup window when
no information exists to be retrieved. Through careful planning, you can
avoid unnecessary steps and create macros that run more quickly and are
easier to debug.
Be sure to test each macro after you record it to ensure that it is working properly.
Editing macros
Sometimes when you are recording macros you will make a mistake or
forget a step. In these situations, you can use a text editor or word processor
to edit the macro file.
We don’t recommend that you write macros from scratch. Instead, you should use
the macro system to record mouse and keyboard actions, then edit your macros if
necessary.
You can use a text editor or word processor to edit the macro directly. Be
sure you save the macro as a text file with carriage returns at the end of each
line, and assign the file the extension .MAC.
Macro files can become quite large. If you want to edit large macros, you will need
to use a text editor or word processor capable of editing large text files.
Running macros
When you’re running macros, it’s sometimes convenient to store the macro-
related warnings and messages (such as the time a macro took to complete)
in a file, rather than displaying them on the screen. To have a macro’s
messages stored in a file, edit the macro and add the Logging command as
the first line of the macro. Refer to the Logging command in the online help
for specific details about how to use this command.
Keys.ini file
You can use a special file named Keys.ini when running macros with the
runtime engine. The Keys.ini file contains macro commands that are run at
startup to configure various system behaviors. The macro commands in the
Keys.ini are run only if the file is included in the parameters for the
command to launch the runtime engine.
You create the Keys.ini file using a text editor, such as Notepad. The
Keys.ini file is typically stored in the Data folder of your application
installation.
To work properly, the Keys.ini file must also have a blank line as the last item in the
file.
The following macro commands are typically used in the Keys.ini file:
• SetEbDateTo
• SetEbTimeTo
• Logging
• SetExitAction
• MacroError
• FilePath
310 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 3 4 T E S T I N G Y O U R A P PL I C A T I O N
• The macro language is case-sensitive, as are the names of all fields, win-
dows, forms and other resources.
• Each statement must end with a carriage return.
• Comments in the macro appear with a pound sign (#) preceding the
comment. Comments are ignored when the macro is run.
• Names containing spaces, as well as values entered using the keyboard,
must be enclosed in single quotation marks.
A sample macro is shown in the following illustration.
312 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 3 4 T E S T I N G Y O U R A P PL I C A T I O N
• Locales
• Designing for eventual translation
• Window design
• Report design
• Coding issues
• International defaults file settings
Locales
You may wish to think of potential international markets in terms of locales,
not as countries or languages. Some countries may encompass several
languages within their borders, and some languages may be used in
different ways, depending upon the country. Similarly, some locales may
encompass more than one country, depending upon the product being sold.
In addition to language differences, different locales may handle currencies,
dates and times differently.
For example, if you plan to market your application in the United States
and Canada, you may need to make relatively few changes to your
application’s interface to customize the product for either locale. You may
need to update a few string resources to ensure that appropriate
terminology is used – such as “cheque” in Canada and “check” in the
United States. You also may wish to incorporate multicurrency
functionality to enable your customers to handle transactions in either
Canadian or United States dollars, but you wouldn’t have to do so unless
your customers are likely to engage in international business.
However, marketing your application in both the United States and Mexico
would probably require more interface changes, because the official
languages and the currency denominators are different. If you design with
both locales in mind from the beginning, you may be able to minimize the
effort and resources required to adapt your product later.
Even if the interface changes are relatively minor, you may need to significantly
revise parts of your application if it deals with taxes or payroll. Be sure to research
these issues thoroughly when you’re localizing your application.
316 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 3 5 C R E A T I N G A N I N TE R N A T I O N A L PR O D U C T
Refer to the description One way to addresses this problem is by adding the International=TRUE
of the International setting to the defaults file and adding an alternate collating sequence text
defaults file setting in file, DEX.ACS, to the same directory where the RUNTIME.EXE file is
the Dexterity online located. The .ACS file provides a list of how the characters ought to be
help for more sorted. As long as the .ACS file is in the correct location and International is
information about set to TRUE, any new tables that are created will use the alternate collating
using alternate sorting sequence. Existing tables must be shrunk to enable the alternate collating
sequences. sequence.
Window design
Many of the issues discussed in Designing for eventual translation on
page 317 describe changes that will appear in windows in your application.
These issues may be more immediately relevant if you’re in the process of
preparing your application for international markets. However, keeping
these topics in mind as you begin designing your application will help
make future localization easier.
There are four aspects of window layouts that are likely to be different from
one locale to another.
318 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 3 5 C R E A T I N G A N I N TE R N A T I O N A L PR O D U C T
The actual space required for translated text depends largely upon the target
language, so your individual requirements may vary. Size these elements according
to your anticipated international needs, but recognize that you’ll probably need to
make additional changes to the interface regardless of your initial planning.
Using graphics
When designing graphic elements or icons, be aware that some images may
be offensive or inappropriate in certain cultures or contexts. We recommend
that you verify that the graphics in your application are appropriate for the
target country.
Report design
Some of the issues discussed in the Application design section of this chapter
directly affect the layout and functionality of reports in your application.
These issues may be more immediately relevant if you’re in the process of
preparing your application for an international market. However, keeping
these topics in mind as you begin designing your application will help
make future localization easier.
There are six aspects of report layouts that are likely to be different from one
locale to another.
The actual space required for translated text depends largely upon the target
language, so your individual requirements may vary.
You also can adjust the report layout by marking the Best Text Fit option in
the Dexterity Report Definition window. Reports will take longer to print if
this option is marked, so it’s better to rearrange the layout so the report can
be printed in normal text mode.
320 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 3 5 C R E A T I N G A N I N TE R N A T I O N A L PR O D U C T
When you display graphics report layouts, guides will indicate the correct
margins for the currently-selected printer and page size. You can use these
guides to reposition fields on the report. Be aware that a graphics report
created according to the specifications for a particular printer may not be
printed correctly when using a different printer. It’s best to create graphics
reports only when you can be sure of the type of printer that will be used to
print the report.
Using graphics
As with graphic elements used in windows, be aware that some images
may be offensive or inappropriate in certain cultures or contexts.
Coding issues
This section addresses common coding issues to consider as you’re
completing initial development. By planning your development carefully,
you’ll be able to write scripts for your application that will require minimal
adaptation for international markets and allow you to shorten development
time for those additional markets.
String-related issues
String-related coding issues typically arise when translating an application
into another language in the process of localization. By following the
guidelines listed here, you will minimize the amount of additional effort
required to localize your application.
Dexterity will display compiler warnings every time you use a hard-coded
string in a script.
You can avoid string overflow errors by storing the integer value of the
message, rather than the message resource itself, as shown in the
following example.
322 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 3 5 C R E A T I N G A N I N TE R N A T I O N A L PR O D U C T
If a script contains many hard-coded strings, you may want to use a pragma at
the beginning of the script to turn off the literal string warnings, and then
turn them back on at the end of the script.
The Currency function library allows you to define your own custom
currency formats. Refer to the currency function library in the Dexterity
Script Reference manual or the Dexterity online help for more
information about these functions.
324 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 3 5 C R E A T I N G A N I N TE R N A T I O N A L PR O D U C T
Setting Result
International TRUE: Specifies that the application will use the alternate
collating sequence.
FALSE: Specifies that the application will use the standard
ASCII collating sequence. False is the default value.
NativeFontLeading TRUE: Specifies that the application will use the full native
font height (leading) in Windows.
FALSE: Specifies that the application will use a shortened
font height in Windows. False is the default value.
Font This setting allows you to specify equivalent Central
European fonts for each of Dexterity’s built-in fonts. Valid
settings include:
FontArial=Arial CE
FontCourier New=Courier New CE
FontTimes New Roman=Times New Roman CE
Tolerance -1: Allows you to prevent reports from using a printer’s built-
in font. Use this setting in conjunction with the Font setting
to ensure that Central European fonts will be used when
printing reports.
328 P R O G R A M M E R ’ S G U I D E V O L U M E 2
Chapter 36: Introduction to COM
Integrating with other applications can be challenging. To make integration
easier, support for the Component Object Model (COM) has been
implemented in Dexterity. Information about COM is divided into the
following sections:
• Automation
• Classes and objects
• Events
Automation
Automation is the general term that describes how applications use COM to
interact with each other. An application can act as an automation server or an
automation client.
When a Dexterity-based
application acts as an automation
server, other COM-enabled COM-enabled Dexterity-based
applications can control it. application application
When a Dexterity-based
application acts as an automation
client, it can control other COM- Excel
Dexterity-based
enabled applications such as Excel.
application
Applications that implement COM typically have classes for each of the
items you can work with in the application. You will use the appropriate
class to create an object for each item you want to work with. Once the
object is created, you will use the object’s methods and properties to
interact with the object. For example, Microsoft Excel has classes for each of
the items you typically work with, such as worksheets and charts.
Worksheet Object
An application creates an
object for each item that it will
work with. Objects are based
on the classes implemented
Application
by the COM application.
Chart Object
330 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 3 6 IN T R O D U C T IO N TO C O M
Events
Applications that provide access through COM may also implement events,
which can notify other applications when specific actions occur. For
example, Microsoft Excel provides several workbook events that allow
other applications to be notified when certain activities occur for
workbooks.
• Type libraries
• Referencing type libraries
Type libraries
A type library is a special type of file that uses a standard format to describe
the interfaces, classes, methods, properties, and events an application
makes available through COM. Most applications that implement COM
will provide a type library that describes the implementation.
Type library files have the file extension .TLB or .OLB. It’s also possible for
type library information to be embedded into executable files or DLLs.
When a COM-enabled application is installed, it typically adds an entry to
the Windows Registry that tells the system where the type library
information for the application can be found. This makes it easy for other
tools, such as Dexterity, to find out what COM-enabled applications are
installed on the current system.
Adding to a dictionary
Use the Library Definition window to add a COM type library reference to
the current dictionary.
If the type library file hasn’t been registered, click the Path lookup
button in the Library Definition window. Use the File dialog to select
the type library file for the application you want to reference.
334 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 3 7 C O M L I B R A R I E S
Once the type library reference has been added to the dictionary, the Data
Type Definition window, and all scripts in the application will be able to
access the information in the type library.
Adding to a script
To add a type library reference to a specific script, you will use the import
statement. This statement creates a reference to the specified type library
file, allowing the current script to access the methods, properties, and other
items described in the type library file.
Typically, you would use the import statement only if you were using the
COM functionality in a limited number of scripts in your application, or
were using pass-through sanScript. In all other cases, we recommend
creating a type library reference for the entire dictionary.
• COM references
• Generic references
• Creating COM references
COM references
A COM reference is also known as a “typed” reference, because it can refer
only to a COM object of a specified type. When you create a COM reference,
you must specify the class of the item that the reference will refer to. For
example, you could create a COM reference that refers to an Excel
workbook. The type library for an application specifies the types of objects
a COM reference can refer to in that application.
Using COM references can also improve the performance of your COM
integration. Because COM type information is contained in the reference,
the script compiler can use “early binding”. This means all of the necessary
information to connect to the specified object must be retrieved only once,
when the script is compiled.
Generic references
Refer to Chapter 25, A generic reference is also known as an “untyped” reference. It can refer to
“References,” for more any type of COM resource. It could also refer to a resource in the
information about application dictionary, such as a table or window. Prior to Dexterity 7.0, all
using generic references were generic references.
references.
We don’t recommend using generic references to refer to COM objects.
Generic references to COM objects must be used carefully. Because these
references contain no type information, there is no compile-time checking
to verify the reference is being used correctly. If a reference refers to a
different type of object than you are expecting, a runtime exception may
occur.
Your application may not perform as well if you use generic references,
because the information necessary to connect to the specified object isn’t
available at compile-time. Generic references always use “late binding”,
where the information to connect to the specified COM object must be
retrieved at runtime.
Data types
Both global and local data types can refer to COM objects. To create a data
type that refers to a specific type of COM object, use the following
procedure.
338 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 3 8 R E F E R E N C I N G C O M O B J E C T S
Click Select. The name of COM object type you selected will appear in
the COM Object Type field.
Notice that the type used for the variable is Acrobat.CAcroAVDoc, which
indicates the variable can refer only to objects that are of the type
CAcroAVDoc as defined in the Acrobat type library.
You can use the Class Browser window from within the Script Editor to
make looking up COM object types much easier. This is described in Class
Browser on page 345.
340 P R O G R A M M E R ’ S G U I D E V O L U M E 2
Chapter 39: COM in sanScript
Most of the interaction with a COM-enabled application is controlled
through sanScript code. This portion of the documentation describes the
various aspects of utilizing COM within scripts. Information is divided into
the following sections:
• Objects
• Properties and methods
• Constants
• Data type conversion
• Class Browser
• Collections
• COM exceptions
• Debugging COM integrations
Objects
Typically, the first action a script must perform when working with a COM-
enabled application is to create or retrieve an object to work with.
Creating objects
There are two ways of creating objects: the new statement and the
COM_CreateObject() function. The following example shows how to use
the COM_CreateObject() function to create a new instance of Microsoft
Word.
app = COM_CreateObject("Word.Application");
if app = null then
error "Could not create Word application object.";
end if;
• The new statement uses a COM reference to specify the COM object to
create, while the COM_CreateObject() function uses a string value.
This means the object type can be verified at compile-time for the new
statement.
Instead, you must use the Add() method for the workbook object:
chart = workbook.Charts.Add();
Guidelines
Keep in mind the following guidelines when creating objects:
• If a library doesn’t contain methods to create new objects, try the new
statement to create the object.
• If the new statement doesn't work to create an object, then use the
COM_CreateObject() function.
342 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 3 9 C O M I N S A N S C R I P T
Retrieving objects
In some cases, the application you want to interact with may already be
running. You can use the COM_GetObject() function to retrieve an object
for an application that is already running.
Destroying objects
When you have finished working with a COM object, you should properly
dispose of the object. Dexterity keeps track of how many times a specific
object is being referenced. An object will exist as long as there is a valid
reference to it.
app = COM_GetObject("Excel.Application");
If a COM method has optional parameters that you don’t want to supply,
you can use the none keyword in place of the parameter value. If the
optional parameters are at the end of the parameter list, you can simply
exclude them from the method call.
Constants
Many COM libraries provide constant values you can use while working
with the properties and methods in the library. To use a constant from the
library, use the dot notation to indicate the library that defines the constant.
The following example uses the wdWindowStateMinimize constant to
minimize the active window in Microsoft Word. Notice that the constant
uses the dot notation to qualify that it is defined in the Word object library.
Always use COM constants when possible. They make your COM code much
easier to read.
344 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 3 9 C O M I N S A N S C R I P T
Class Browser
The Class Browser window is useful for looking up COM methods,
properties, and constants while you are writing scripts. From the Script
Editor, you can access the Class Browser window by choosing Class
Browser from the Script menu.
Select the COM library from which you want to look up information. Use
the Classes and Members lists to select individual items. A description of
the selected item appears at the bottom of the window. Click Copy to copy
the fully-qualified name of the selected item to the clipboard. You can paste
the name into the current script.
Some COM libraries provide online help that describes the members in the
library. If help for a specific library is installed, the Help button will be
enabled in the Class Browser window.
Collections
In COM, objects of the same type are often grouped into special objects
called collections. For example, Microsoft Excel has a Workbooks object,
which is a collection of all of the open workbooks in the application.
You cannot directly access the values of a collection from sanScript. Instead,
you must use additional properties and methods to access the items in a
collection. Often the Item method or the Item property can be used to
access the items in a collection. For example, the following sanScript code
retrieves the first item from the Fields collection of an ADO recordset.
Notice that the Item property must be used to access the item in the
collection.
In many cases, the Item method or property is hidden, so it won’t be visible in the
Class Browser window until you mark the Show Hidden option.
COM exceptions
The COM integrations you write may encounter errors (exceptions) as they
run. Use the exception handling capability in Dexterity to deal with COM
exceptions that occur. To learn more about exception handling, refer to
Chapter 26, “Structured Exception Handler.”
346 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 3 9 C O M I N S A N S C R I P T
To find what COM exception occurred, scroll down in the error dialog that
displays the COM exception. The exception subclass will be listed, and
indicates the error that occurred.
If you’re having problems with a COM integration, use the Windows Task
Manager to list all of the processes currently running. If you see multiple
processes for the application you’re integrating with, use the End Process
button to terminate them, and then try your integration again. If you
continue to have problems, restart both Dexterity and the application
you’re integrating with to eliminate any COM communication issues.
using System.Runtime.InteropServices;
348 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 3 9 C O M I N S A N S C R I P T
[ClassInterface(ClassInterfaceType.AutoDual)]
public class Customer {
...
}
Your code must follow other guidelines for your assemblies to be accessible
from COM. Refer to the .NET Framework documentation for more
information about COM interoperability.
regasm Customer.dll
This is the type library you will reference from within Dexterity to
access the capabilities made available through COM.
The GUIDs in the registry (.reg) file and the .NET assembly must match for
the .NET assembly to be accessed. Be sure the .reg file is generated for the
assembly you are deploying.
When your application accesses the assembly you created with .NET, the
assembly should be located in the same directory as Dexterity or the
Runtime engine. Another option is to be sure the assembly is strongly-
named, so it can be added to the global assembly cache (GAC) where it can
be located by any application accessing it through COM.
350 P R O G R A M M E R ’ S G U I D E V O L U M E 2
Chapter 40: Callbacks and Events
You can create callback objects in your Dexterity-based application. Like any
other COM objects, callback objects have properties and methods that you
define. Other applications use COM to access the properties and methods
in the callback object. Callback objects are used for the following:
• Callback objects
• Accessing callback objects
• Events
• Creating an event handler
Callback objects
Callback objects are created at runtime, using several functions from the
Dexterity object function library. To create a callback, you must create the
callback object, add methods and properties, and make the object available
to other applications.
Adding properties
To add properties to the callback object, use the DexObject_AddProperty()
function. Properties store specific data items in the callback object. Their
values can be set or retrieved by both the Dexterity-based application and
external applications accessing the callback object through COM.
Adding methods
To add methods to the callback object, use the DexObject_AddMethod()
function. Methods on the callback object can be called from the Dexterity-
based application, but are primarily called by other applications accessing
the callback object through COM. Methods are the primary way other
applications can initiate actions in your application.
• Use “in” parameters for values that will be passed from the external
application into the callback method.
• Use “inout” parameters for values that can pass values from the
external application into the callback method, and also return a value
from the method.
Note that other applications have no way of determining the name of any
callback object, or the properties and methods that are part of the object. If
you want other applications to be able to access the callback objects you
create, you must provide documentation for the callback.
352 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 4 0 C AL LB A C K S A N D E V E N T S
• The names of the properties in the callback, along with their associated
data types.
• The names of the methods in the callback, along with the return value
(if any), the parameters for the methods, and their associated data
types.
You can also throw exceptions in the user-defined function associated with
the method. Any exceptions you throw will be propagated back through
COM to the application that called the method in the callback object. For
example the following sanScript code in the user-defined function
associated with a method will produce a COM exception. The value 1001 is
passed as the exception class, and the value 1 is passed as the exception
subclass.
throw 1001, 1, "User not logged in";
We recommend that you use values greater than 1000, but less than 32,767 for the
exception class.
Notice that the exception class is passed in as the error number, while the
exception subclass is enclosed in brackets and prepended to the exception
message.
Events
Some applications that implement COM interfaces also provide events that
can notify other applications of specific actions. For example, Microsoft
Excel provides several events for worksheets that notify other applications
of actions such as a selection change in the worksheet. The Class Browser
window lists the events that an application is making available.
354 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 4 0 C AL LB A C K S A N D E V E N T S
• Create a callback object and add a method to the callback object for the
event.
User-defined function
You need to create a user-defined function for each event that you want to
be notified of. The function will run each time the event occurs in the
application you are integrating with. The parameters for the user-defined
function must match those specified for the event. For example, the
following is the description of the SheetSelectionChange event available
for an Excel workbook.
Notice that this event returns two parameters, but no actual return value for
the method. The first parameter is a reference to the worksheet for which
the event occurred. The second parameter is range of cells selected in the
worksheet.
Function SelectionChangedEvent
in reference worksheet;
in Excel.Range selection;
Callback object
To respond to a COM event from another application, you must create a
callback object that contains the method or property necessary for the
event. The name of the method or property in the callback must exactly
match the name of the event.
Continuing the previous example, the following code creates the callback
object to handle the SheetSelectionChange event. Notice in the
DexObject_AddMethod() function that the name of the method added to
the callback is the same as the name of the event. Also note that the
COM_AttachEventHandler() function is used to associate the event for the
active Excel worksheet with the corresponding callback object in the
Dexterity-based application.
356 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 4 0 C AL LB A C K S A N D E V E N T S
Once the callback object has been created and attached to the event, your
application will begin receiving and processing events. Events will be
processed until either application is shut down, or you detach the event
handler with the COM_DetachEventHandler() function.
Responding to events
When an event occurs, the corresponding method will be called in the
callback object within the Dexterity-based application. Often, it’s useful to
be able to access the callback object from within the user-defined function
that is run for the event. To access the properties or methods for the current
callback object, use the this reference.
Troubleshooting
If the event handler isn’t processing events, check the following:
• Verify the name you used for the method in the callback exactly
matches the event name. The name is case-sensitive.
• Be sure the parameter list and return value for the user-defined
function match the parameter list and return value for the event. If the
parameter types don’t match, the event handler can’t be attached to the
callback object.
360 P R O G R A M M E R ’ S G U I D E V O L U M E 2
Chapter 41: Microsoft Word
To automate Microsoft Word, use the Microsoft Word Object Library.
Information about automating Word is divided into the following sections:
The following examples are written for version 10 of the Microsoft Word
Object Library (Word XP). If you use a different version of the object library,
you may need to change the code to have it work properly.
• Application object
• Document object
• Paragraph object
• Table object
• Cell object
• Range object
Refer to the help file for the Microsoft Word Object Library for a complete
description of all of the objects contained in the library.
To use the Microsoft Word Object Library, you should first create an
Application object with a sanScript function. For more information on
creating COM objects with sanScript functions, refer to Chapter 39, “COM
in sanScript.” You can create all of the other objects from methods provided
in the library.
Paragraph object
Range object
Create the remaining Table object
objects from methods
provided in the library. Cell object
Range object
Range object
Range object
Declared variables
In the script examples, it is assumed that you have declared the following
variables:
Application object
The Application object allows you to work with the Word application.
362 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 4 1 M IC R O S O F T WO R D
When using Distributed COM (DCOM), create a new instance of Word with
the COM_CreateObject() function. In the following example, a new
instance of Word is created on the computer “SystemServer”.
{Show an application.}
app.Visible = true;
{Hide an application.}
app.Visible = false;
{Hide messages.}
app.DisplayAlerts = Word.wdAlertsNone;
Hiding warnings prevents the user from seeing the messages displayed by an
application. Your code must still handle the errors or other exceptional conditions
that caused Word to display warning messages.
Closing an application
After you have finished using an application, you can close it with the Quit
method.
{Close an application.}
app.Quit();
Constant Behavior
wdDoNotSaveChanges Doesn’t save the changes in the open documents
wdSaveChanges Saves changes without prompting the user
wdPromptToSaveChanges Asks the user if they would like to save changes
{Save the open documents without prompting the user. Then close the
application.}
app.Quit(Word.wdSaveChanges);
Document object
The Document object allows you to work with a document. The
Documents collection contains all of the currently open documents in a
Word application.
364 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 4 1 M IC R O S O F T WO R D
Saving a document
To save a new document, use the SaveAs method.
If you have previously saved a document, use the Save method to save the
document.
You can use the Saved property to find out if a document has been saved. If
the Saved property returns true, the document has been saved. Otherwise,
it has not.
You can also set the value of the Saved property. If you set the Saved property to
true without saving a document, any messages prompting you to save the
document are suppressed.
Closing a document
Use the Close method to close a document. You can use the constants
contained in the wdSaveOptions enumeration to specify whether a
document is saved when it is closed.
Range object
A Range object specifies an area in a document. Once you have created a
Range object, you can insert text, tables, and paragraphs into a document.
Specifying a range
You can use the Range method from the Document object to specify a
range in a document. You can specify the beginning and ending characters
of the range.
If you do not specify beginning or ending characters, the returned range is the
entire document.
You can also use the Range property from a Paragraph object, a Cell
object, or a Table object to specify a range. The returned Range object
spans from the beginning to the end of the object associated with the
property.
Inserting text
To insert text before a range, use the InsertBefore method.
366 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 4 1 M IC R O S O F T WO R D
If you are inserting text after a range retrieved from a Paragraph object, the text
becomes part of the next paragraph.
Paragraph object
The Paragraph object allows you to work with the paragraphs in a
document. The Paragraphs collection contains all of the paragraphs in a
document.
You can add a new paragraph to the document with the InsertParagraph
method. The InsertParagraph method replaces the Range object with the
range of the new paragraph.
Retrieving a paragraph
You can retrieve a paragraph from the Paragraphs collection with the Item
method by the number associated with the paragraph.
You can use the First property to retrieve the first paragraph in the
Paragraphs collection.
You can use the Last property to access the last paragraph in the
Paragraphs collection.
Formatting a paragraph
The Paragraph object contains several properties to modify the appearance
of a paragraph, such as the Alignment property and the Format property.
368 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 4 1 M IC R O S O F T WO R D
Table object
The Table object allow you to work with a table. The Tables collection
contains all of the tables in a document.
Creating a table
Use the Add method to add a new table to a document and to the Tables
collection. You must specify the location of the table as well as the
dimensions of the table.
Retrieving a table
You can retrieve a table from the Tables collection with the Item method by
the number associated with the table.
Formatting a table
The Table object contains several methods and properties to modify the
appearance of a table, such as the AutoFormat method, the Borders
property, the Shading property, and the Spacing property.
{Set the width of the inside borders of the table to 0.25 points.}
wordTable.Borders.InsideLineWidth = Word.wdLineWidth025pt;
You can also access individual borders in a table with the Borders
collection. To specify a border, use the Item method and the constants
contained in the wdBorderType enumeration.
{Set the line style for the bottom border of the table.}
wordTable.Borders.Item(Word.wdBorderBottom).LineStyle =
➥ Word.wdLineStyleDouble;
Spacing property The Spacing property sets the spacing between the
cells in the table. You must supply the spacing between the cells in points.
Cell object
The Cell object allows you to change the number of cells in a table as well
as to format individuals cell.
Retrieving a cell
To retrieve a Cell object from a table, use the Cell method. You need
specify the row and column number of the cell.
{Retrieve a cell from the third row and fourth column in a table.}
cell = wordTable.Cell(3,4);
370 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 4 1 M IC R O S O F T WO R D
To split a cell, use the Split method. You can supply the number of rows,
the number of columns, or both the number of rows and the number of
columns that you would like to create from the cell.
Script example
The following example uses objects from the Microsoft Word Object Library
to create a table in a new document, RESM.DOC. The script adds a record
displayed in the Buyers window of the Real Estate Sales Manager
application to a row in the table.
{Add a table with two rows and four columns to the document.}
wordRange = document.Range(0,0);
wordTable = document.Tables.Add(wordRange, 2, 4);
{Set the values of the cells in the first row in the table.}
wordRange = wordTable.Cell(1,1).Range;
wordRange.InsertAfter("Buyer ID");
wordRange = wordTable.Cell(1,2).Range;
wordRange.InsertAfter("Buyer First Name");
wordRange = wordTable.Cell(1,3).Range;
wordRange.InsertAfter("Buyer Last Name");
wordRange = wordTable.Cell(1,4).Range;
wordRange.InsertAfter("Phone");
{If record is not displayed, warn user. Otherwise, add the record.}
if 'Buyer ID' = "" then
warning "Please display a record in the Buyers window.";
else
wordRange = wordTable.Cell(2,1).Range;
wordRange.InsertAfter('Buyer ID');
wordRange = wordTable.Cell(2,2).Range;
wordRange.InsertAfter('Buyer First Name');
wordRange = wordTable.Cell(2,3).Range;
wordRange.InsertAfter('Buyer Last Name');
wordRange = wordTable.Cell(2,4).Range;
wordRange.InsertAfter(Phone);
end if;
372 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 4 1 M IC R O S O F T WO R D
{Close the document and prompt the user for a document name.}
document.Close(Word.wdPromptToSaveChanges);
The following examples are written for version 10 of the Microsoft Excel
Object Library (Excel XP). If you use a different version of the object library,
you may need to change the code to have it work properly.
• Application object
• Workbook object
• Worksheet object
• Range object
• WorksheetFunction object
• Chart object
Refer to the help file for the Microsoft Excel Object Library for a complete
description of all of the objects contained in the library.
To use the Microsoft Excel Object Library, you should first create an
Application object with a sanScript function. For more information on
creating COM objects with sanScript functions, refer to Chapter 39, “COM
in sanScript.” You can create all of the other objects from methods provided
in the library.
Workbook object
Create the remaining objects from
Worksheet object
methods provided with the library.
Range object
WorksheetFunction object
Declared variables
In the script examples, it is assumed that you have declared the following
variables:
Application object
The Application object allows you to work with the Excel application.
376 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 4 2 M I C R O S O F T E X C E L
Hiding warnings prevents the user from seeing the messages displayed by an
application. Your code must still handle the errors or other exceptional conditions
that caused Excel to display warning messages.
Closing an application
After you have finished using the application, you can close it with the Quit
method.
Workbook object
The Workbook object allows you to work with a workbook. The
Workbooks collection contains all of the currently open workbooks in an
Excel application.
378 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 4 2 M I C R O S O F T E X C E L
Saving a workbook
To save a new workbook, use the SaveAs method.
If you have previously saved a workbook, use the Save method to save the
workbook.
You can use the Saved property to find out if a workbook has been saved. If
the Saved property returns true, the workbook has been saved. Otherwise,
it has not.
You can also set the value of the Saved property. If you set the Saved property to
true without saving a workbook, any message prompting you to save the workbook
is suppressed.
Closing a workbook
Use the Close method to close a workbook.
You can also use the Close method to save a workbook before closing it.
You must specify whether to save the workbook and where to save the
workbook.
Worksheet object
The Worksheet object allows you to work with a worksheet. The
Worksheets collection contains all of the worksheets in a workbook.
Activating a worksheet
A workbook can contain several worksheets. To activate a specific
worksheet, use the Activate method.
380 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 4 2 M I C R O S O F T E X C E L
Moving a worksheet
Use the Move method to move a worksheet to another position in the
workbook. You can place a worksheet either before or after another
worksheet by specifying the “Before” and “After” parameters. Specify the
“Before” parameter to move a worksheet before another worksheet.
{Place a new worksheet after the first worksheet in the workbook. The
"After" parameter refers to the first worksheet, and the "Before"
parameter is set to none.}
worksheet = workbook.Worksheets.Add();
worksheet.Move(none, workbook.Worksheets.Item[1]);
Saving a worksheet
To save a worksheet as a new workbook, use the SaveAs method.
Range object
A Range object specifies a cell or a group of cells on a worksheet. Once you
have created a Range object, you can format a cell or group of cells.
Specifying a range
You can use the Range property from the Worksheet object to specify a
range in a worksheet.
You can also use the Columns property to specify a range. The Columns
property can be used with three different objects: the Application object,
the Worksheet object, and the Range object. The Columns property from
the Application object returns a range containing all of the columns in the
active worksheet.
You can also use the Rows property to specify a range. The Rows property
can also be used with three different objects: the Application object, the
Worksheet object, and the Range object. The Rows property from the
Application object returns a range containing all of the rows in the active
worksheet.
The Rows property from the Worksheet object returns a range containing
all of the rows on the worksheet.
382 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 4 2 M I C R O S O F T E X C E L
You can also use the Value property to retrieve the unformatted value of a
specified range. If the range contains multiple cells, the Value property
returns the value from the active cell.
To retrieve the formatted value of a cell as a string, use the Text property.
Font property The Font property returns a Font object. The Font object
contains properties that modify characteristics of the font, such as style and
size.
You can also access individual borders in a range with the Borders
collection. To specify a border, use the Item property and the constants
contained in the xlBordersIndex enumeration.
{Use a constant from the xlLineStyle enumeration to set the line style
for the bottom border.}
cellRange.Borders.Item[Excel.xlEdgeBottom].LineStyle = Excel.xlDot;
WorksheetFunction object
The WorksheetFunction object allows you to perform calculations on the
cells in a worksheet.
{Count the number of cells that contain the value "Air Conditioned".}
count = wsFunction.CountA(cellRange, "Air Conditioned");
The syntax for worksheet functions are listed in the help file for the Microsoft Excel
Object Library.
384 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 4 2 M I C R O S O F T E X C E L
Chart object
The Chart object allows you to create, modify, and format a chart. The
Charts collection contains all of the currently open chart sheets in a
workbook. It does not contain charts that are embedded in worksheets.
Creating a chart
Use the Add method to create a new chart and add it to the Charts
collection.
Retrieving a chart
You can retrieve a chart from the Charts collection with the Item property
by the number associated with a chart.
The Charts collection does not contain charts that you have embedded in a
worksheet. You must use the ChartObjects method to retrieve an
embedded chart as a ChartObject object. You must provide either the name
or number associated with the chart.
{Specify a range.}
cellRange = worksheet.Range["A1:B5"];
{Set the source data.}
chart.SetSourceData(cellRange);
{Create a 3D pie chart from the values in the specified cellRange. The
chart does not have a legend and is titled "Sales Performance".}
chart.ChartWizard(cellRange, Excel.xl3DPie, none, none, none, none,
➥ false, "Sales Peformance", none, none, none);
The ChartWizard method does not accept all of the values from the xlChartType
enumeration. Refer to the Microsoft Excel Object Library help file for more
information.
386 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 4 2 M I C R O S O F T E X C E L
Script example
The following example uses objects from the Microsoft Excel Object Library
to add a worksheet and embed a chart in a new workbook, RESM.XLS.
{Set the values of the cells in the first row in the table.}
cellRange = worksheet.Range["A1"];
cellRange.Value = "Agent ID";
cellRange = worksheet.Range["B1"];
cellRange.Value = "Agent Name";
cellRange = worksheet.Range["C1"];
cellRange.Value = "March Sales";
cellRange = worksheet.Range["D1"];
cellRange.Value = "April Sales";
cellRange = worksheet.Range["E1"];
cellRange.Value = "May Sales";
388 P R O G R A M M E R ’ S G U I D E V O L U M E 2
Chapter 43: Microsoft ADOX
To create new databases, use the Microsoft ActiveX® Data Objects
Extensions for Data Definition Language and Security (ADOX) Object
Library. Information about using the Microsoft ADOX Object Library is
divided into the following sections:
The following examples are written for version 2.6 of the Microsoft ADOX
Object Library. If you use a different version of the object library, you may
need to change the code to have it work properly.
Refer to the help file for the Microsoft ADOX Object Library for a complete
description of all of the objects contained in the library.
To use the Microsoft ADOX Object Library, you should first create a Catalog
object with a sanScript function. For more information on creating COM
objects with sanScript functions, refer to Chapter 39, “COM in sanScript.”
You can create all of the other objects from methods provided in the library.
Column object
Declared variables
In the script examples, it is assumed that you have declared the following
variables:
Catalog object
The Catalog object allows you to work with groups, users, views, tables,
and procedures that define the database.
390 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 4 3 M I C R O S O F T A D O X
When using Distributed COM (DCOM), create a new Catalog object with
the COM_CreateObject() function. In the following example, a new
Catalog object is created on the computer “SystemServer”.
Creating a Catalog object doesn’t create a database. You must also use the Create
method to create a new catalog.
{Create a catalog.}
conString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=db.mdb";
catalog.Create(conStringr);
In the previous example, the name of the database is db.mdb, and the
database is a Microsoft Jet database. For more information about specifying
database types, refer to the help file for the Microsoft ADOX Object Library.
Creating a catalog also creates a Connection object from the Microsoft ActiveX
Data Objects (ADO) Object Library. When you close the catalog, you should also
close the connection. If you don’t, you may get unpredictable results.
Retrieving a connection
Refer to Chapter 44, Creating a new catalog also creates or opens a Microsoft ADO connection.
“Microsoft ADO,” for Use the ActiveConnection property to retrieve a connection from a Catalog
more information on object.
ADO Connection
objects. {Retrieve the active connection.}
connection = catalog.ActiveConnection;
Be sure to close all connections to a database. If you don’t, you may get
unpredictable results.
Table object
The Table object allows you define the structure of a table in a catalog. The
Tables collection contains all of the tables in a catalog.
You can also access the tables by the name of the table.
You can also use the Name property to set the name of a table.
392 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 4 3 M I C R O S O F T A D O X
Deleting a table
To delete a table from a catalog, use the Delete method. You can delete a
table by the number associated with the table.
Key object
The Key object allows you to define the keys for a table. The Keys
collection contains all of the keys for a table.
You can also use the Name property to set the name of a key.
You can also use the Type property to set the type of a key.
Column object
The Column object allows you to define the fields contained in a table or a
key. The Columns collection contains all of the columns for a key or a table.
394 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 4 3 M I C R O S O F T A D O X
The following table lists the Dexterity data types and the corresponding
ADOX types.
When you create a table, create the table columns before you create key
columns. The columns in a key must also be in the table that the key is
associated with.
Script example
The following example uses objects from the Microsoft ADOX Object
Library to create a table, named “BuyerInfo”, and a primary key for the
table.
{Create a table.}
adoxTable = catalog.Tables.Append("BuyerInfo");
396 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 4 3 M I C R O S O F T A D O X
{Clear variables.}
clear tableKey;
clear adoxTable;
clear catalog;
clear connection;
The following examples are written for version 2.6 of the Microsoft ADO
Object Library. If you use a different version of the object library, you may
need to change the code to have it work properly.
Refer to the help file for the Microsoft ADO Object Library for a complete
description of all of the objects contained in the library.
To use the Microsoft ADO Object Library, you must first create either a
Connection object or a Recordset object with a sanScript function. For
more information on creating COM objects with sanScript functions, refer
to Chapter 39, “COM in sanScript.” You can create all of the other objects
from methods provided in the library.
Declared variables
In the script examples, it is assumed that you have declared the following
variables:
Connection object
To access the data stored in databases, you must first connect to the
database. The Connection object connects your Dexterity-based
application to an existing database.
400 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 4 4 M I C R O S O F T A DO
Opening a connection
Use the Open method to open a connection to a database. You must specify
both the database type and the name of an existing database in the
connection string.
In the previous example, the name of the database is db.mdb, and the
specified database type is a Microsoft Jet database. For more information
about specifying database types, refer to the help file for the Microsoft ADO
Object Library.
Retrieving a connection
For more information When a Microsoft ADOX Object Library catalog is created, a connection to
on the Catalog object, the database is also created. You can retrieve the connection with the
refer to Chapter 43, ActiveConnection property from the Catalog object.
“Microsoft ADOX.”
{Retrieve a connection from a catalog object.}
connection = catalog.ActiveConnection;
Closing a connection
Use the Close method to close a connection.
{Close a connection.}
connection.Close();
Be sure to close all connections to a database. If you don’t, you may get
unpredictable results.
Recordset object
The Recordset object allows you to work with the records returned from a
query on a table.
When using Distributed COM (DCOM), create a new Recordset object with
the COM_CreateObject() function. In the following example, a new
Recordset object is created on the computer “SystemServer”.
Creating a Recordset object doesn’t create a recordset for a table. You must also
use the Open method to add records from a table to a recordset.
Opening a recordset
Use the Open method to add records to a recordset. The Open method
allows you to use a SQL query to select records from tables in a database.
To specify how you work with records in the database, use values from the
CursorTypeEnum enumeration. To specify how the database is locked, use values
from the LockTypeEnum enumeration.
Use the MoveFirst method to move to the first record in the recordset.
Use the BOF property to find out if you are at the beginning of a recordset.
402 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 4 4 M I C R O S O F T A DO
Use the EOF property to find out if you are at the end of a recordset.
Adding a record
To add a new record to a recordset, you must call the AddNew method and
set the values of the fields in the record. Then you must call the Update
method to add the record to the recordset.
Updating a record
To update a record in a recordset, you must move to the record and set the
values for the fields that you want to update in the record. Then you must
call the Update method to save the changes.
Deleting a record
To delete a record in a recordset, you must move to the record you want to
delete and call the Delete method. Then you must call the Update method
to update the recordset.
Closing a recordset
Use the Close method to close a recordset.
{Close a recordset.}
recordset.Close();
Field object
The Field object allows you to work with the fields in a record. The Fields
collection contains all of the fields in a record.
Retrieving a field
You can retrieve a field from the Fields collection with the Item property
by the number associated with the field.
404 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 4 4 M I C R O S O F T A DO
You can also use the Value property to set the value of a field.
Script example
The following example uses objects from the Microsoft ADO Object Library
to add a record displayed in the Buyers window of the Real Estate Sales
Manager application to the “BuyerInfo” table created in the Script example
on page 396.
{If the record isn’t displayed, warn the user. Otherwise, add the
record to the table.}
if 'Buyer ID' = "" then
warning "Please display a record in the Buyers window.";
else
recordset.AddNew();
recordset.Fields.Item[0].Value = 'Buyer ID';
recordset.Fields.Item[1].Value = 'Buyer First Name';
recordset.Fields.Item[2].Value = 'Buyer Last Name';
recordset.Fields.Item[3].Value = Phone;
recordset.Update();
end if;
406 P R O G R A M M E R ’ S G U I D E V O L U M E 2
Chapter 45: Microsoft Internet Explorer
To automate Internet Explorer, use the Microsoft Internet Controls Object
Library. Information about automating Internet Explorer is divided into the
following sections:
Refer to the help file for the Microsoft Internet Controls Object Library for a
complete description of all of the objects contained in the library.
To use the Microsoft Internet Controls Object Library, you must first create
an InternetExplorer object with a sanScript function. For more information
on creating COM objects with sanScript functions, refer to Chapter 39,
“COM in sanScript.”
Declared variables
In the script examples, it is assumed that you have declared the following
variable:
InternetExplorer object
The InternetExplorer object allows you to work with an Internet Explorer
application.
The new keyword and the COM_CreateObject function return a hidden instance
of an application. Use the Visible property to show a hidden application.
408 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H AP TE R 4 5 M IC R O S O FT I N TE R N E T E X PL O R E R
You can also load a file with the Navigate method by specifying the full
pathname of the file.
Setting the Silent property to false shows the dialog boxes. This is the
default setting.
Hiding dialog boxes prevents the user from seeing warning messages and other
prompts displayed by an application. Your code still must handle the errors or other
exceptional conditions that caused Internet Explorer to display dialog boxes.
Closing an application
After you have finished using the application, you can close it with the Quit
method.
Script example
The following example uses the InternetExplorer object from the Microsoft
Internet Controls Object Library to load an XML file.
{Clear variable.}
clear IExplorer;
410 P R O G R A M M E R ’ S G U I D E V O L U M E 2
Chapter 46: Microsoft Outlook
To automate Microsoft Outlook, use the Microsoft Outlook Object Library.
Information about automating Outlook is divided into the following
sections:
The following examples are written for version 10 of the Microsoft Outlook
Object Library (Outlook XP). If you use a different version of the object
library, you may need to change the code to have it work properly.
• Application object
• NameSpace object
• MAPIFolder object
• MailItem object
• Attachment object
• Recipient object
• ContactItem object
Refer to the help file for the Microsoft Outlook Object Library for a complete
description of all of the objects contained in the library.
MAPIFolder object
MailItem object
Create the remaining objects from
methods provided with the library. Attachment object
Recipient object
ContactItem object
Declared variables
In the script examples, it is assumed that you have declared the following
variables:
412 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 4 6 M I C R O S O F T O U TL O O K
Outlook security
Outlook’s security prevents a script from automatically accessing a folder
or sending an e-mail without the user’s permission. When a script accesses
a folder or sends an e-mail, Outlook displays a warning message and asks if
the script should be permitted to access the folder or send the e-mail. The
user can then permit the script to access the folder or e-mail for a specified
length of time.
Application object
The Application object allows you to work with the Outlook application.
Closing an application
After you have finished using an application, you can close it with the Quit
method.
{Close an application.}
app.Quit();
NameSpace object
The NameSpace object represents the root folder in Outlook. It allows you
to work directly with default folders and to specify the characteristics of the
current Outlook session.
Retrieving a namespace
Use the GetNameSpace method from the Application object to retrieve a
namespace. You must specify “MAPI” as the namespace type.
{Retrieve a namespace.}
nameSpace = app.GetNameSpace("MAPI");
Logging on to a session
Use the Logon method to log on to a session. You can specify the profile
name and the password.
414 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 4 6 M I C R O S O F T O U TL O O K
MAPIFolder object
The MAPIFolder object allows you to work with Outlook folders. The
Folders collection contains all of the folders contained in the namespace or
the current folder.
olFolderCalendar olFolderJournal
olFolderContacts olFolderNotes
olFolderDrafts olFolderTasks
olFolderInbox
If you do not specify a constant, the folder type is set to the same type as the
parent folder.
The namespace contains a fixed list of default folders. You cannot add any
additional folders to a namespace.
You can also use the GetDefaultFolder method from the NameSpace
object to retrieve the default folders. You must specify the folder item type
with a constant from the olDefaultFolders enumeration.
You can also retrieve the folder’s display name with the Name property.
Displaying a folder
Use the Display method to display a folder in a window.
Deleting a folder
Use the Delete method from the MAPIFolder object to delete a folder.
416 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 4 6 M I C R O S O F T O U TL O O K
You can also use the Remove method to delete a folder from the Folders
collection. You must supply the number associated with the folder.
Folders that are deleted are sent to the Deleted Items folder. However, if a folder is
deleted from the Deleted Items folder, it is permanently deleted.
MailItem object
The MailItem object allows you to work with a mail message. An Items
collection is associated with each MAPIFolder object. The collection
contains all of the mail messages in the folder.
You can also use the Add method from the Items collection to create a new
mail message. You must specify the item type with the olMailItem
constant.
You can also access a mail message by the mail message’s subject.
Adding a recipient
To add a new recipient to a mail message, create a new Recipient object or
add a new recipient to the Recipients collection. For more information,
refer to Recipient object on page 421.
Adding an attachment
To add a new attachment to a mail message, you must add a new
Attachment object to the Attachments collection. For more information,
refer to Attachment object on page 420.
418 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 4 6 M I C R O S O F T O U TL O O K
You can use the Sent property to find out if a mail message has been sent.
The Sent property returns true if the mail message has been sent.
Otherwise, it returns false. You cannot set the value of the Sent property.
To save a new mail message, use the SaveAs method. You must specify the
filename for the mail message. You can also specify the saved message’s file
type with a constant from the olSaveTypeAs enumeration. If you do not
specify a constant, the mail message is saved as the message file type.
You can use the Saved property to find out if a mail message has been
saved. The Saved property returns true if the mail message has been saved.
Otherwise, it returns false. You cannot set the value of the Saved property.
{Close the mail message and save the message in the Drafts folder.}
mailItem.Close(Outlook.olSave);
Attachment object
The Attachment object allows you to work with an attachment. The
Attachments collection contains all of the attachments associated with a
mail message.
Adding an attachment
Use the Add method to add an attachment to the Attachments collection.
You must specify the full pathname of the attachment. You can also specify
the attachment type, the position number of the attachment, or the display
name of the attachment. The attachment type is specified with a constant
from the olAttachmentType enumeration,
420 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 4 6 M I C R O S O F T O U TL O O K
Removing an attachment
Use the Delete method from the Attachment object to remove an
attachment from a mail message.
You can also use the Remove method from the Attachments collection to
remove an attachment from a mail message. You must supply the number
associated with the attachment.
Recipient object
The Recipient object allows you to work with a user in Outlook. The
Recipients collection contains all of the recipients in an Outlook item, such
as a mail message.
Adding a recipient
Use the Add method to add a recipient to the Recipients collection. You
must specify the display name of the recipient.
Creating a new recipient does not ensure that the recipient has an entry in the
Outlook address book. Use the Resolve method to verify that an entry exists in the
address book for a recipient.
Retrieving a recipient
You can retrieve a recipient from the Recipients collection with the Item
method by the number associated with the recipient.
You can also access a recipient by the display name of the recipient.
You can also use the ResolveAll method from the Recipients collection to
resolve all of the recipients’ addresses simultaneously. The ResolveAll
method returns true if all of the addresses are resolved. Otherwise, it
returns false.
422 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 4 6 M I C R O S O F T O U TL O O K
Use the Resolved property to find out if a recipient’s address has been
resolved. The following example warns the user if a recipient in the
Recipients collection has not been resolved.
Deleting a recipient
Use the Delete method from the Recipient object to delete a recipient from
a mail message.
You can also use the Remove method to delete a recipient from the
Recipients collection. You must supply the number associated with the
recipient.
ContactItem object
The ContactItem object allows you to work with a contact. An Items
collection is associated with each MAPIFolder object. The collection
contains all of the contact items in the folder.
You can also use the Add method from the Items collection to create a new
contact. You must specify the item type with the olContactItem constant.
424 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 4 6 M I C R O S O F T O U TL O O K
Displaying a contact
Use the Display method to display a contact in a window.
You can also use the FirstName property to retrieve the first name of a
contact.
You can also use the LastName property to retrieve the last name of a
contact.
You can also use the JobTitle property to retrieve the job title of a contact.
To save a new contact, use the SaveAs method. You must supply the
filename for the contact. You can also specify the saved contact’s file type
with a constant from the olSaveAsType enumeration. If you do not specify
a constant from the olSaveAsType enumeration, the contact is saved as the
message file type.
You can use the Saved property to find out if a contact has been saved. The
Saved property returns true if the contact has been saved. Otherwise, it
returns false. You cannot set the value of the Saved property.
Deleting a contact
To delete a contact, use the Delete method.
Contacts that are deleted are sent to the Deleted Items folder. However, if a contact
is deleted from the Deleted Items folder, it is permanently deleted.
Closing a contact
Use the Close method to close a contact. You can use the constants
contained in the olInspectorSave enumeration to specify the whether a
contact is saved when it is closed. To save a contact as a contact, use the
Close method and specify the olSave constant.
{Close the contact and save all changes without asking the user.}
contactItem.Close(Outlook.olSave);
426 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 4 6 M I C R O S O F T O U TL O O K
Script example
The following example uses objects from the Microsoft Outlook Object
Library to create a mail message, resolve the recipient’s e-mail address, and
send the mail message if the recipient’s e-mail address has been resolved.
{Get a namespace.}
nameSpace = app.GetNameSpace("MAPI");
{If the recipient’s address has been resolved, send the mail message.
Otherwise, warn the user that the message cannot be sent.}
if(resolved) then
mailItem.Send();
else
warning "Cannot send message. E-mail address has not been"
➥ + "resolved.";
end if;
{Close Outlook.}
app.Quit();
{Clear variables.}
clear mailItem;
clear folder;
clear nameSpace;
clear app;
428 P R O G R A M M E R ’ S G U I D E V O L U M E 2
Chapter 47: Microsoft XML
To work with XML, use the Microsoft XML Object Library. Information
about using the Microsoft XML Object Library is divided into the following
sections:
The following examples are written for version 5.0 of the Microsoft XML
Object Library. If you use a different version of the object library, you may
need to change the code for it to work properly.
• DOMDocument object
• IXMLDOMNode object
• IXMLDOMNodeList object
• IXMLDOMProcessingInstruction object
• IXMLDOMElement object
• IXMLDOMText object
Refer to the help file for the Microsoft XML Object Library for a complete
description of all of the objects contained in the library.
To use the Microsoft XML Object Library, you must first create a
DOMDocument object with a sanScript function. For more information
about creating COM objects with sanScript functions, refer to Chapter 39,
“COM in sanScript.” You can create all of the other objects from methods
provided in the library.
IXMLDOMNodeList object
Create the remaining
objects from methods IXMLDOMProcessingInstruction object
provided with the library.
IXMLDOMElement object
IXMLDOMText object
Declared variables
In the script examples, it is assumed that you have declared the following
variables:
430 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 4 7 M I C R O S O F T X M L
DOMDocument object
The DOMDocument object represents an XML document.
You can use the load method to load an XML file. The load method returns
true if the XML file loads successfully. Otherwise, the method sets the
documentElement property to null and returns false. You can supply either
the pathname or the URL of the XML file.
The loadXML method and the load method discard the current document before
loading a new document. If the method fails, the documentElement property
returns null.
You can also retrieve the document element with the documentElement
property. If a document element does not exist, the property returns null.
Refer to the help file for the Microsoft XML Object Library for more
information on the XSLPattern and XPath selection languages.
432 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 4 7 M I C R O S O F T X M L
IXMLDOMNode object
The IXMLDOMNode object represents a node from an XML document.
An IXMLDOMNode object can contain an IXMLDOMElement object, an
IXMLDOMProcessingInstruction object, or an IXMLDOMText object.
Creating a node with the createNode method does not add the node to an
XML document. You must add the node to an XML document with the
insertBefore method, appendChild method, or the replaceChild method.
Adding a node
Use the insertBefore method to insert a nodes before another node in a
document. You must specify the node you are adding as well as the node
you are adding it in front of.
You can also use the appendChild method to add a node after all of the
other nodes in a document.
Retrieving a node
The childNodes collection contains all of the nodes in an XML document.
You can retrieve a node from the childNodes collection with the item
method by the index associated with the node.
Replacing a node
Use the replaceChild method to replace a node in a document with another
node. The node you are replacing is returned.
Deleting a node
Use the removeNode method to remove a node from a document.
434 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 4 7 M I C R O S O F T X M L
IXMLDOMNodeList object
The IXMLDOMNodeList object refers to a list of IXMLDOMNode
objects. An IXMLDOMNodeList object immediately reflects changes
made to the nodes in the list.
You may use either the XSLPattern or XPath selection languages to specify the
pattern you want to search an element for. For more information on specifying the
selection language, refer to DOMDocument object on page 431.
Retrieving a node
You can retrieve an object from an IXMLNodeList object with the item
method by the index associated with the object.
You can also access all of nodes in the list sequentially with the nextNode
method.
{Loop through the nodes in the node list and remove them from the
document.}
for i=1 to nodeList.length do
node = nodeList.nextNode();
xmlDoc.removeChild(node);
end for;
Use the reset method to allow the nextNode method to return the first
node in the list.
IXMLDOMProcessingInstruction object
The IXMLDOMProcessingInstruction object represents a processing
instruction, or information that specifies how an application should
interpret an XML document. An example of a processing instruction is a
document prolog, which specifies the XML version of a document. The
prolog can also specify the character encoding of a document and whether a
document requires additional information to be interpreted correctly.
436 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 4 7 M I C R O S O F T X M L
IXMLDOMElement object
The IXMLDOMElement object represents an element in an XML
document.
Creating an element
Use the createElement method from the DOMDocument object to create a
new element. You must supply the name of the element.
Creating a new element with the createElement method does not add the
element to an XML document. You must add the element to an XML
document or another element with the appendChild method, the
insertBefore method, or the replaceChild method.
Adding an element
Use the insertBefore method to insert an element before another element or
text node. You must specify the element that you are adding as well as the
element or text node that you are adding the element in front of.
You can also use the appendChild method to add an element after all
elements and text nodes in a document.
Retrieving an element
The childNodes collection contains all of the nodes in an XML document.
You retrieve a node from the childNodes collection with the item method
by the index associated with the object. To find an element in the
childNodes collection, retrieve each object as an IXMLDOMNode object,
and then test the object’s node type with the nodeType property.
{Retrieve the first node in the childNodes collection and find out if
it is an IXMLDOMElement object.}
node = xmlDoc.childNodes.item[0];
if(node.nodeType = MSXML2.NODE_ELEMENT) then
element = node;
end if;
438 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 4 7 M I C R O S O F T X M L
Refer to You can also use the selectSingleNode method to retrieve an element. The
IXMLDOMNode object selectSingleNode method searches an element for a specified pattern and
on page 433 for more returns an IXMLDOMNode object representing the first instance of the
information on pattern. If no element matches the pattern, null is returned.
IXMLDOMNode
objects. You may use either the XSLPattern or XPath selection languages to specify the
pattern you want to search for. Refer to the help file for the Microsoft XML Object
Library for more information on the XSLPattern and XPath selection languages.
The following example uses the selection language XPath to search for the
first Seller_First_Name element. For more information on specifying the
selection language, refer to DOMDocument object on page 431.
Replacing an element
Use the replaceChild method to replace an node in an XML docuement
with an element. The node that you are replacing is returned.
Normalizing an element
Use the normalize method to normalize an IXMLDOMElement object and
its descendants. Normalizing an element combines adjacent text nodes into
a single text node. When you normalize an element, text nodes can only be
separated by markup, such as tags, comments, and processing instructions.
Deleting an element
Use the removeChild method to remove an element from a document or
another element.
IXMLDOMText object
An IXMLDOMText object represents the text content of an element. It is
commonly referred to as a text node.
Creating a new text node with the createTextNode method does not add
the text node to an XML document. You must add the text node to an
element with the appendChild method, the insertChild method, or the
replaceChild method.
440 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 4 7 M I C R O S O F T X M L
You can also use the appendChild method to add a text node after all
elements and text nodes.
You can also use the data property to set the data stored in a text node.
When you set the data stored in a text node with the data property, you replace
the data previously stored in the text node.
{Add the string " Barr" to the data stored in the text node.}
textNode.appendData(" Barr");
You can also use the insertData method to add data to a text node at a
specified position.
{Add the string "Robert " to the text node at the fifth position.}
textNode.insertData(5, "Robert ");
442 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 4 7 M I C R O S O F T X M L
Script example
The following example uses objects from the Microsoft XML Object Library
to create an XML document from a record displayed in the Sellers window
of the Real Estate Sales Manager application. The XML document is saved
as SELLERS.XML.
{If a record is not displayed, warn the user. Otherwise, create an XML
document.}
if 'Seller ID' = "" then
warning "Please display a record in the Sellers window.";
else
{Create a text node containing data from the Seller ID field and
append it to the Seller ID element.}
textNode = xmlDoc.createTextNode(str('Seller ID'));
fieldElement.appendChild(textNode);
{Create a text node containing data from the Seller First Name
field and append it to the Seller First Name element.}
textNode = xmlDoc.createTextNode(str('Seller First Name'));
fieldElement.appendChild(textNode);
{Create a text node containing data from the Seller Last Name
field and append it to the Seller Last Name element.}
textNode = xmlDoc.createTextNode(str('Seller Last Name'));
fieldElement.appendChild(textNode);
{Create a text node containing data from the Phone field and
append it to the Phone element.}
textNode = xmlDoc.createTextNode(str(Phone));
fieldElement.appendChild(textNode);
444 P R O G R A M M E R ’ S G U I D E V O L U M E 2
C H A P T E R 4 7 M I C R O S O F T X M L
The following example uses objects from the Microsoft XML Object Library
to parse an XML document and construct a Buyer_Data record in the Real
Estate Sales Manager application.
{Retrieve the 'BuyerID' element and set the 'Buyer ID' field to the
text node’s data.}
dataNode = rootNode.selectSingleNode("BuyerID");
'Buyer ID' of table Buyer_Data =
➥ string(dataNode.childNodes.item[0].text);
{Retrieve the 'BuyerFirstName' element and set the 'Buyer First Name'
field to the text node’s data.}
dataNode = rootNode.selectSingleNode("BuyerFirstName");
'Buyer First Name' of table Buyer_Data =
➥ string(dataNode.childNodes.item[0].text);
{Retrieve the 'Buyer_Last_Name' element and set the 'Buyer Last Name'
field to the text node’s data.}
dataNode = rootNode.selectSingleNode("Buyer_Last_Name");
'Buyer Last Name' of table Buyer_Data =
➥ string(dataNode.childNodes.item[0].text);
{Retrieve the 'Buyer_Last_Name' element and set the 'Buyer Last Name'
field to the text node’s data.}
dataNode = rootNode.selectSingleNode("Buyer_Last_Name");
'Buyer Last Name' of table Buyer_Data =
➥ string(dataNode.childNodes.item[0].text);
{Retrieve the 'Agent' element and set the 'Agent' field to the text
node’s data.}
dataNode = rootNode.selectSingleNode("Agent");
Agent of table Buyer_Data = string(dataNode.childNodes.item[0].text);
446 P R O G R A M M E R ’ S G U I D E V O L U M E 2
APPENDIX
Appendix
The following appendixes are provided:
448 P R O G R A M M E R ’ S G U I D E V O L U M E 2
Appendix A: Dexterity Limits
The following is a list of limits for the Dexterity development system.
450 P R O G R A M M E R ’ S G U I D E V O L U M E 2
A P PE N D I X A D E XT E R IT Y LI M IT S
Unprintable characters
In the following table, the unprintable ASCII characters are listed along
with their meaning. Many of these unprintable characters serve different
functions that vary from one operating system to another.
456 P R O G R A M M E R ’ S G U I D E V O L U M E 2
Glossary Alert message
A message that appears when
AutoLinkTable property
A Dexterity feature that allows the
inappropriate, inadequate or unclear Modifier to add fields to a window from
3-D highlight data or instructions are issued, when data the auto-linked table. Each window can
An Appearance setting for push buttons, is not accessible or when a confirmation have only one auto-linked table.
and button drop lists that causes them to is required.
have a flat appearance until the mouse Auto-chunk
pointer is moved over the button. Then Alphanumeric A utility in Dexterity Utilities that
the button appears with a 3-D border. A combination of numbers and letters. simplifies the process of creating a single
dictionary chunk from an application
Accelerator key Anonymous tables and fields dictionary.
A key or set of keys on the keyboard that Refers to procedures where the names of
can be used as a “shortcut” to select a the tables and/or fields that will be Auto-generated stored
menu or menu option rather than using passed to the script aren’t known when procedures
the mouse. the procedure is written. Anonymous Stored procedures automatically created
tables and fields can make procedures by Dexterity to optimize database
Access key more generic and more useful. performance for SQL tables.
An underlined character in a menu name
or item that allows users to select the Array field Background tasks
item by typing the underlined character A field containing multiple occurrences Processing tasks, such as printing a
or by holding down the ALT key and of the same type of information. The report or running a procedure, that occur
typing the character. individual pieces of information stored while allowing other tasks to be
by an array field are called elements. For completed simultaneously, such as
Active locking example, a seven-element array field entering data in a window. For instance, a
A method of locking that ensures only could be used to store daily sales totals procedure can be run in the background
one user can change or delete the instead of seven individual fields. using the background parameter of the
contents of a record at one time. The data call statement.
in the locked record can’t be changed or Array index
deleted by another user until the lock is The number designating a specific Big line item
released. The lock for the record is element within an array field. Each The area containing the fields that will be
released when the user with the active component field within an array field has displayed for a scrolling window in
lock moves to another record or closes an array index. expanded mode. When the scrolling
the table. window is in standard mode, only fields
When creating calculated fields
within the area marked by the small line
Add-line containing array fields, legends passed
item will be displayed. The mode for a
using the run report statement can be
The blank line at the bottom of an adds- scrolling window is determined with the
used as the array field’s index.
allowed scrolling window that allows expand statement.
you to enter new information using the Ask dialog box
scrolling window. Block
A modal dialog box generated by the
A specified portion of the dictionary. For
Additional headers and footers ask() function. A dialog box displays a
instance, an unused “block” within a
text string, an information icon and up to
Report sections that are printed when a dictionary refers to a portion of the
three push buttons allowing the user to
specified field changes. Additional dictionary that isn’t being used, or a
headers and footers are created using the make a selection.
portion that at one time contained
Report Options and Header/Footer AutoCopy property information that has been deleted. You
Options windows. A Dexterity feature that simplifies can remove unused blocks by using the
copying information between a table compress utility of Dexterity Utilities.
Adds-allowed scrolling window
buffer and a window.
A scrolling window that allows the user Boolean expression
to enter and save new information using An expression that results in a value of
the scrolling window. true or false.
458 P R O G R A M M E R ’ S G U I D E V O L U M E 2
G L O S S A R Y
460 P R O G R A M M E R ’ S G U I D E V O L U M E 2
G L O S S A R Y
462 P R O G R A M M E R ’ S G U I D E V O L U M E 2
G L O S S A R Y
Menu item
The list will appear with scroll bars if the
One of the selections associated with a
information in the list is greater than the
particular menu.
size of the list box field when it’s added
to the layout window. An integer Loop Message (resource)
position - not the static text - for the A statement in script that runs
A message that was entered using the
corresponding selection in a list box is repetitively until a defined condition is
Dexterity Messages window and that can
stored when the selection is saved to a met. SanScript supports three loop
be referenced by its ID in scripts, using
table. structures: for, while and repeat.
the getmsg() function. The message can
Macro system be displayed using the ask() function or
List field the warning or error statement.
Any field that uses a list box, drop-down A tool used to play and record macros in
list, multi-select list, button drop list, Dexterity or Dexterity applications.
Modal dialog
combo box or visual switch data type. Macros allow users to automate tasks
A window that contains no operating
throughout an application.
system controls and can’t be resized.
List view Modal dialogs are used when you require
A control type used for fields that display Mail enabling
A connectivity technology that allows the user to perform an action before
data in icon form or as a multicolumn list.
reports in a Dexterity application to be continuing.
mailed to other users. Dexterity
Modifiable key
applications can send items through
A key for which the values stored in the
MAPI-compliant mail systems.
key segments can be changed. If a key
Main Menu form isn’t modifiable, changing the contents of
The first form that opens when an a field that is part of the key will generate
application dictionary is run. The form an error and result in the record not being
must use the name Main Menu, without saved.
Local field any additional words or characters.
A field that’s available only within the Modifier
Stand-alone applications must have a
form in which it’s created. Local fields are A Dexterity tool that allows an
Main Menu form. Integrating
used as window fields. See also Global application’s interface to be modified by
applications use the Main Menu form for
field. an end user. A forms dictionary stores
the main application.
these modifications.
Local variable Main product
A variable specific to a single script that Modulus
A Dexterity-based application that can
is active only while the script is running. A numeric operator (%) that returns the
operate on its own. Some main product
Local variables are defined at the remainder of the division of two
applications, such as Microsoft Dynamics
beginning of the script. numbers. For example 15 % 4 returns the
GP are multidictionary capable, allowing
value 3.
Locking other Dexterity-based applications to
integrate with them.
The process of reserving the use of a
record in a table. Locking can be passive, Main window (of a form)
allowing others users to change or delete The window of a form that when closed,
the record, or active, which doesn’t allow closes the form and any other windows
other users to change or delete the record. associated with the form.
464 P R O G R A M M E R ’ S G U I D E V O L U M E 2
G L O S S A R Y
466 P R O G R A M M E R ’ S G U I D E V O L U M E 2
G L O S S A R Y
Relationship
See Table relationship.
A group of radio buttons must be used
with a radio group.
468 P R O G R A M M E R ’ S G U I D E V O L U M E 2
G L O S S A R Y
Series resources the source code control system. Source to the application. In parameters can be
Lists containing the names of forms, files are stored in the repository. passed to stored procedures. In addition
reports, tables and table groups in an to the status code, out parameters also
application dictionary. These lists are
Stand-alone application can be returned.
A Dexterity application that operates
used for security, palettes and table
maintenance. These lists are updated
without integrating with another String
automatically by Dexterity. You can use product. A sequence of up to 255 ASCII characters.
See also String resources.
Dexterity Utilities to update series Standard composite
resources manually. A composite that can have up to nine String expression
Session ID components. See also Extended composite. An expression that results in a string
value.
A unique identification code assigned by Starting script
Dexterity each time a client logs into a One of the two scripts associated with a String resources
SQL database. Applies only when a SQL dictionary chunk. When you create a Sequences of up to 79 characters used
database type is used. dictionary chunk, you can specify a throughout an application dictionary for
Shortcut key procedure from the application to be the window names, field prompts, static text
starting script for the dictionary chunk. values and report text.
A key combination that will activate a
menu item, equivalent to selecting the The starting script will be run with the
other starting and ending scripts after all Structured Query Language
item with the mouse. (SQL)
dictionary chunks have been unchunked.
Shrinking See also Ending script and Installation A language that allows you to define,
A process of copying the contents of one script. manipulate and control access to data in a
table (the original table) to an empty relational database.
duplicate table (the shrink table) to
Statement
A script command used to complete a Synchronize (Utility)
remove excess space in the original table.
specific action in an application, such as A feature that checks all relationships
This process copies only the data; any
running a report, opening a window or between resources in a dictionary and
existing space that may exist in the recalculates all buffer sizes.
original table doesn’t get copied to the saving an item to a table.
shrink table. Static picture value Synchronize (Dexterity)
A picture that’s displayed as part of a In Dexterity, to specify the native picture
Simple name on each platform that should be used as
data type, such as the picture on a button
The name is given to the object when it is
drop list. the static value for a single data type.
created. See also Qualified name. When two pictures are synchronized,
Small line item Static text value Dexterity will assign them the same
Text that’s displayed as part of a data internal ID. Thereafter, when a
The area containing the fields that will be
displayed when a scrolling window is in type, such as the name of a push button synchronized picture is displayed by the
or the items in a list box. application as it runs, the native picture
standard mode. When a scrolling
window is in expanded mode, additional that’s appropriate to the current
Storage type operating system will be displayed.
fields below the small line item mark, but One of the standard forms used to store
above the big line item mark, will be the data in a field. The storage types are: System command
displayed. A scrolling window’s mode is boolean, integer, long, currency, variable Prior to Dexterity 3.0, a sanScript
determined using the expand statement. currency, string, text, date, and time. The command (indicated by a three- or four-
Source code control server control type determines which storage digit number) that used the system
Software that allows Dexterity clients to type is used to store the data in the field. statement. System commands were
communicate with the source code replaced with function libraries in
Stored procedure Dexterity 3.0.
control provider. Queries written in SQL, then compiled
Source files and stored in a database. Called from a
script using the call sproc statement,
The physical files containing source code
or other resources that are managed by stored procedures perform server-based
processing tasks, and return a status code
470 P R O G R A M M E R ’ S G U I D E V O L U M E 2
G L O S S A R Y
Variable WordWrap
A script item that allows an application A property for text fields that causes text
to store values. They’re called variables to automatically continue to the next line
because their values can change. For when it extends beyond the right edge of
example, a variable could be used to store the field.
the value returned from a dialog box
created with the ask() function. WYSIWYG
An acronym meaning “what you see is
Version numbers (for dictionary what you get.” This applies to intuitive
chunks) design tools used to create windows and
A dictionary chunk can have a version reports in Dexterity.
number associated with it. When the
dictionary chunk is unchunked, the
Zoom pointer
A pointing hand cursor that appears
version numbers are written to the
when the pointer is over a push button
application dictionary. The version
numbers can be retrieved from the field that has the Zoom property set to
True. In Microsoft Dynamics GP, clicking
dictionary and typically are displayed in
the mouse when this pointer is over the
the application’s About Box.
field enables users to “zoom” to the
Virtual table window where records for the field can
A type of table that acts as a single, read- be added.
only, non-physical table. A virtual table
accesses member tables by utilizing a
related field from a primary table and
using it as a key in a secondary table.
Window
The work area used to enter and display
information in an application.
Window field
A global or local field that has been
added to a window layout.
474 P R O G R A M M E R ’ S G U I D E V O L U M E 2
I N D E X
476 P R O G R A M M E R ’ S G U I D E V O L U M E 2
I N D E X
478 P R O G R A M M E R ’ S G U I D E V O L U M E 2
I N D E X
480 P R O G R A M M E R ’ S G U I D E V O L U M E 2
I N D E X
Microsoft Excel Object Library Microsoft Outlook (continued) Microsoft Outlook Object Library
(continued) deleting (continued)
Range property 381 folders 416 Delete method 421
Save method 379 mail messages 420 with contacts 426
SaveAs method recipients 423 with mail messages 420
with workbooks 379 displaying with MAPI folders 416
with worksheets 381 contacts 425 with recipients 423
Saved property 379 folders 416 Display method
script example 387 mail messages 418 with contacts 425
SetSourceData method 385 integrating with Dexterity-based with mail messages 418
Text property 382 applications 411-428 with MAPI folders 416
Value property 382 logging off 414 FirstName property 425
Visible property 377 logging on 414 GetDefaultFolder method 415
Workbook objects 378 opening 413 GetNameSpace method 414
Worksheet objects 380 removing attachments 421 Importance property 418
WorksheetFunction objects 384 resolving recipients’ addresses 422 Index property
WorksheetFunction property 384 saving with attachments 421
Microsoft Internet Controls Object attachments 421 with recipients 422
Library contacts 426 Item method
InternetExplorer objects 408 mail messages 419 with attachments 420
Navigate method 408 security 413 with contacts 424
overview 407-410 sending mail messages 419 with mail messages 417
Quit method 409 setting body of mail messages 418 with MAPI folders 415
Refresh method 409 setting importance of mail with recipients 422
script example 410 messages 418 JobTitle property 425
Silent property 409 setting subject of mail messages LastName property 425
Stop method 409 418 Logoff method 414
Visible property 408 Microsoft Outlook Object Library Logon method 414
Microsoft Internet Explorer Add method MailItem objects 417
closing 409 with attachments 420 MAPIFolder objects 415
creating 408 with contacts 424 Name property
integrating with Dexterity-based with mail messages 417 with MAPI folders 416
applications 407-410 with MAPI folders 415 with recipients 423
loading web pages 408 with recipients 421 NameSpace objects 414
refreshing web pages 409 Address property 423 overview 411-428
Microsoft Outlook Application objects 413 Quit method 413
adding, recipients 421 Attachment objects 420 Recipient objects 421
closing 413 Body property 418 Remove method 421
closing contacts 426 Close method with MAPI folders 416
closing mail messages 420 with contacts 426 with recipients 423
creating with mail messages 420 Resolve method 422
attachments 420 ContactItem objects 424 ResolveAll method 422
contacts 424 CreateItem method Resolved property 422
folders 415 with contacts 424 Save method
mail messages 417 with mail messages 417 with contacts 426
deleting CurrentUser property 414 with mail messages 419
contacts 426 DefaultItemType property 416
Microsoft Outlook Object Library Microsoft Word Object Library Microsoft XML (continued)
(continued) (continued) creating
SaveAs method Hyphenation property 368 processing instructions 436
with contacts 426 InsertAfter method 366 text nodes 440
with mail messages 419 InsertBefore method 366 integrating with Dexterity-based
SaveAsFile method 421 InsertParagraph method 367 applications 429-446
Saved property InsertParagraphAfter method 367 loading XML documents 431
with contacts 426 InsertParagraphBefore method normalizing elements 440
with mail messages 419 367 removing
script example 427 Item method data from text nodes 442
security 413 with documents 365 elements 440
Send method 419 with paragraphs 368 nodes 434
Sent property 419 with tables 369 processing instructions 437
Subject property 418 Last property 368 text nodes 442
Type property 423 Merge method 371 replacing
Microsoft Word Open method 365 elements 439
closing 364 overview 361-373 nodes 434
closing documents 366 Paragraph objects 367 processing instructions 437
creating documents 364 Quit method 364 text nodes 441
formatting paragraphs 368 Range method 366 retrieving
formatting tables 369 Range objects 366 elements 438
inserting tables 369 Range property nodes 434
inserting text 366 with cells 366 processing instructions 437
integrating with Dexterity-based with paragraphs 366 text nodes 441
applications 361-373 with tables 366 saving XML documents 433
merging table cells 371 Save method 365 specifying selection languages 432
opening 362 SaveAs method 365 Microsoft XML Object Library
opening documents 365 Saved property 365 appendChild method
saving documents 365 script example 371 with elements 438
splitting table cells 371 SetHeight method 371 with nodes 433
Microsoft Word Object Library SetWidth method 371 with text nodes 441
Add method Shading property 370 appendData method 442
with documents 364 Spacing property 370 childNodes collection
with paragraphs 367 Split method 371 with elements 438
with tables 369 Table objects 369 with nodes 434
Alignment property 368 Visible property 363 with processing instructions
Application objects 362 Microsoft XML 437
AutoFormat method 369 adding with text nodes 441
Borders property 369 data to text nodes 442 createElement method 437
Cell method 370 elements 438 createNode method 433
Cell objects 370 nodes 433 createProcessingInstruction
Close method 366 processing instructions 436 method 436
DisplayAlerts property 363 text nodes 441 createTextNode method 440
Document objects 364 copying XML documents 432 data property 442
First property 368 creating deleteData method 442
FitText property 371 elements 437 documentElement property 432
Format property 368 nodes 433 DOMDocument objects 431
482 P R O G R A M M E R ’ S G U I D E V O L U M E 2
I N D E X
Microsoft XML Object Library Microsoft XML Object Library Names window (continued)
(continued) (continued) using to find resource names 27
insertBefore method selectNodes method 438 NameSpace objects, with COM 414
with elements 438 selectSingleNode method 438 native pathnames, defined 465
with nodes 433 setProperty method 432 native pictures, defined 465
with processing instructions text property 434 .NET assemblies, accessing through
436 xml property COM 348
with text nodes 441 with elements 440 networks, testing 301
insertData method 442 with XML documents 432 new symbol 3
item method 435 modal dialogs, defined 464 nodes, for tree view fields
with elements 438 modifiable keys, defined 464 adding 123
with nodes 434 Modifier, defined 464 attributes 119
with processing instructions modulus operator defined 465
437 defined 464 described 118
with text nodes 441 use in sanScript 19 NoExecutableCode pragma 224
IXMLDOMElement objects 437 mouse enter scripts, attaching 40 normal mode
IXMLDOMNode objects 433 mouse exit scripts, attaching 40 for scrolling windows 96,
IXMLDOMNodeList objects 435 moving, focus with scripts 54 illustration 96
IXMLDOMProcessingInstruction multidictionary, defined 465 switching to expanded mode 96
objects 436 multiple format selector, defined 465 nothing keyword, for user-defined
IXMLDOMText objects 440 multiplication operator, use in functions 210
length property 441 sanScript 19 numeric expressions 16
load method 431 MultiSel, field property 133 defined 465
loadXML method 431 multi-select list box, defined 465 overflow in 55
nextNode method 435 multiuser processing
nodeType property adding to applications 176 O
with elements 438 chapter 175-180 object properties
with processing instructions deleting a record example 180 for list view fields 133
437 described 175 for tree view fields 120
with text nodes 441 locking scenarios 176 objects
normalize method 440 retrieving a record example 178 creating 341
overview 429-446 saving a record example 179 creation guidelines 342
removeNode method described 330
with elements 440 N destroying 343
with nodes 434 Name, scrolling window property 95, naming in scripts 8
with processing instructions 109 retrieving 343
437 names one-to-many relationship, defined 465
with text nodes 442 automatic script naming 35 one-to-one relationship, defined 465
replaceChild method finding with the Names window online help
with elements 439 27 for sanScript 34
with nodes 434 objects in scripts 8 from the script editor 34
with processing instructions scripts 35 testing 302
437 scrolling windows 92, 108 Open Database Connectivity (ODBC),
with text nodes 441 Names window defined 465
reset method 435 defined 465 Open Script button, described 255
save method 432 described 27 Open Script window 256
script example 443 illustration 28
484 P R O G R A M M E R ’ S G U I D E V O L U M E 2
I N D E X
process groups (continued) radio buttons (continued) Recordset objects, with COM 401
defined 466 using 73 recursion, defined 467
deleting 207 radio groups reference information, viewing 282
described 205 defined 467 reference information table
guidelines 206 tab sequence 73 building 281
naming 205 using 73 clearing 284
processing remotely 206 random numbers, script example 210 described 281
running in the background 206 Range objects updating 284
viewing in the process queue 205 in Excel Object Library 381 Reference Information window 282
Process Monitor window 206 in Word Object Library 366 references
Process Server, defined 466 ranges assigning 232
product IDs, defined 466 chapter 167-171 chapter 231-235
product information, defined 466 creating with the range statement creating 232
Program Structures, chapter 61-63 165 defined 467
progress control, defined 466 described 167 described 231
progress indicator, defined 466 exclusive ranges 169 examples 234
prompts, defined 466 for multisegment keys 168 guidelines 233
properties inclusive ranges 169 reference fields 232
accessing for COM objects 343 overview 167 reference variables 232
adding to callback objects 351 purpose 167 to COM objects 337
defined 467 range types 169 typed 337
for scrolling windows 94 retrieving records with 165 untyped 338
providers, defined 467 script example 167, 168, 169 using 233
push buttons well-behaved ranges, described references button 282
3-D border 72 171 Referencing COM Objects, chapter
3-D highlight 72 read only, defined 467 337-340
Appearance property 72 read/write, defined 467 REGASM.EXE application 349
Cancel property 73 Recipient objects, with COM 421 regression testing, described 300
caption and images 70 record locking reject record, for scrolling windows 102
Default property 72 active 175 relative decimal position, defined 467
defined 467 described 175 releasing locks on records 176
Hyperspace property 73 passive 175 remote procedures, defined 467
pictures for 71 releasing locks 176 remote processing, overview 204
static picture values 71 recording macros repeat...until statement 63
using 70 described 303, 309 replacement markers, defined 467
suspending recording 303 report fields, defined 467
Q records report modules, defined 467
qualified names defined 467 report view, described 131
defined 467 deleting 155, 163 Report Writer, defined 467
described 9 having the same key values 163 reports, testing 301
examples 10 limiting using ranges 167 reports dictionaries, defined 468
using in scripts 9 locking 175-180 repository, defined 468
Quick Reports, defined 467 modifying values of key fields 165 required fields, defined 468
retrieving 154, 160 Reserved Words, appendix 453
R saving 153, 157 Resource Descriptions, defined 468
radio buttons size, defined 467 resource IDs, defined 468
defined 467 working with using scripts 151 Resource Usage, chapter 281-284
tab sequence 73
486 P R O G R A M M E R ’ S G U I D E V O L U M E 2
I N D E X
488 P R O G R A M M E R ’ S G U I D E V O L U M E 2
I N D E X
490 P R O G R A M M E R ’ S G U I D E V O L U M E 2