AdvancedTopicsReferenceGuide
AdvancedTopicsReferenceGuide
SoftVelocity Inc.
www.softvelocity.com
Trademark Acknowledgements:
ii
Table Of Contents
Table Of Contents
Advanced Topics 1
General Topics ...............................................................................................................................................1
Migration Tips .................................................................................................................................................2
Commonly Used Equates ..............................................................................................................................8
Keycode Equates List ................................................................................................................................. 29
Dictionary Class .......................................................................................................................................... 46
FileCallBackInterface and DriverOps .......................................................................................................... 47
API Calls and Advanced Resources 53
Prototypes and Declarations ....................................................................................................................... 53
Accessing Clarion's Runtime Library from C/C++ or Modula-2 Code ......................................................... 55
Standard C Functions in Clarion's Runtime Library .................................................................................... 64
Clarion Language Utilities 73
BeginUnique (Set Application to Run in a Single Process) .................................................................. 74
BLOBTOFILE (Copy Data from BLOB Field to File) ............................................................................ 75
BYTETOHEX (convert a BYTE to Hexadecimal) ................................................................................. 76
CreateDirectory (Create a directory) .................................................................................................... 77
EndUnique (Close an application's event handle) ................................................................................ 79
FileExists (Confirm file existence) ........................................................................................................ 80
FILETOBLOB (Copy data from a file to a BLOB field) ......................................................................... 81
FullDrag (Query/Change Window Drag Setting) .................................................................................. 82
GetFileDate (Get the file date) .............................................................................................................. 83
GetFileTime (Get the file time) ............................................................................................................. 84
GETREG(get Windows registry entry).................................................................................................. 85
GetTempFileName (Generate a temporary file) ................................................................................... 87
GetTempPath (Return TMP or TEMP environment path) .................................................................... 88
GetUserName (Return Network User Name) ....................................................................................... 89
IsTermServer (Verify Terminal Server Usage) ..................................................................................... 90
LONGTOHEX (convert an unsigned LONG to Hexadecimal) .............................................................. 91
PROP:WindowsVersion ........................................................................................................................ 92
PUTREG (write value to Windows registry) ......................................................................................... 93
RemoveDirectory (Remove a directory) ............................................................................................... 96
ResizeImage (Resize an image to fit a control) .................................................................................... 97
SHORTTOHEX (convert an unsigned SHORT to Hexadecimal) ......................................................... 98
ValidateOLE (Validate OLE Control Creation) ..................................................................................... 99
WindowExists (Validate Window Existence) ...................................................................................... 100
Build System 101
Clarion Project System .............................................................................................................................. 101
MSBuild tasks ........................................................................................................................................... 104
.cwproj File Format ............................................................................................................................. 104
CW Task ............................................................................................................................................. 105
Redirection Task ................................................................................................................................. 112
Controlling the IDE from the Command Line 114
User's Guide to ClarionCL.exe (command line interface to the Clarion IDE) ........................................... 114
Legacy (Pre-Clarion 7) Project System Reference 117
Introduction................................................................................................................................................ 117
Language Components ...................................................................................................................... 118
Project System Macros ............................................................................................................................. 119
Setting Macro Values ......................................................................................................................... 120
Special Project System Macros .......................................................................................................... 121
iii
Advanced Programming Topics
General Topics
This section presents miscellaneous topics that are geared to the more advanced developer, and provides a potpourri of
information:
1
Advanced Programming Topics
Migration Tips
This topic is designed as a quick reference for developers and programmers who are migrating pre-Clarion 6
applications from prior versions.
The following are changes in that were originally documented in the Clarion 6 versions that need to be reviewed by all
developers, particularly those migrating applications created prior to Clarion 6:
The error codes posted by the EVALUATE statement have been modified in Clarion 6:
1010 - formerly 800: Bad expression syntax
1011 - formerly 801: Unknown identifier in the expression
1012 - formerly 802: Mismatched POPBIND
The best workaround is to simply change the icon, or just delete the old splash procedure and add a new one using the
Clarion 6 default.
DLL Initialization
Enforcement of threaded variables in multi-DLL applications is critical in Clarion 6. In older versions, if your file definitions
are set to "open in current thread" in the dictionary (the THREAD attribute is set in the FILE definition), your file definitions
in your DLLs must match that definition. To ensure this, examine each application’s global file control section, and make
sure that all of your files are set to 'ALL THREADED' in the Threaded drop list.
Regarding files that are defined across multiple DLLs (e.g., a Customer file defined in a data DLL and again in another
DLL), you CANNOT mix thread and non-threaded attributes on the same files in a multi-dll application. Although
this programming style was permitted in earlier versions of Clarion, the initialization of preemptive threads will not allow
this in Clarion 6.
You can use either setting, thread or non-threaded, as long as it’s consistent across all DLLs and your executable. And, of
course, mixing the threaded and non-threaded attributes among different files is still permitted.
2
Advanced Topics
General Rules regarding your data and the new Thread Model
1. Use the THREAD attribute on global Data (file, class, group, queue or simple types).
2. Use the THREAD attribute on module Data (file, class, group, queue or simple types).
3. Avoid the use of static variables.
4. Don't pass the address of anything within a START command - this was a
common trick used by people to communicate between threads.
If you do any of the above you must make them thread safe. Refer to the Multi Threaded Programming PDF for detailed
information on this process.
Local data (including classes that are normally instantiated locally) is automatically threaded (unless you put the STATIC
attribute on it).
3
Advanced Programming Topics
For example:
GLOB:GROUPONE GROUP,PRE() !`GLOB’ is the prefix of the Global Pool
GLOB:STRING1 STRING(20)
GLOB:STRING2 STRING(20)
END
With Clarion 6, this same group will be generated without the Global prefix, and would appear as follows:
Since it is possible to define multiple Global Pools in your dictionary, a second Global Pool could be created as follows:
This will cause the compiler to generate "Duplicate Label" warnings when migrating your applications. The solution to this
is to add a prefix to each of your GROUPs that share common label names across Global Pools.
For example:
GLOB:GROUPONE GROUP,PRE(GLOB) !`GLOB’ is the prefix of the 1st GROUP
STRING1 STRING(20)
STRING2 STRING(20)
END
4
Advanced Topics
In nearly all cases, the solution is to first export the application to a text file (TXA), and then import it as text into the
Clarion 6 environment.
With this, you can set your data file names in the dictionary to have no path in them, and then set the data path once in
the program start up. From there, each file will inherit the common data path.
With that in mind, dictionaries created in a prior version will continue to work. The only issue is where file names and
structures are stored exclusively in a DLL and referenced from the EXE. In prior versions, you had to define these
objects in the EXE as EXTERNAL, and did not care if the files in this object were threaded or not. In this release, any
objects that contain threaded data must add the THREAD attribute to the object definition.
Mixing threaded and non-threaded data in an object is dangerous and likely to cause problems.
All projects compiled in Clarion 6 are 32-bit. Prior to loading older project files (.PR or .PRJ) into the Clarion 6
environment, load them into your favorite text editor and make sure that the following pragma entry is set properly:
#system win32
You can also load the project into the project editor. When you press OK to close the project, the #system pragma will be
automatically updated.
5
Advanced Programming Topics
It may still be safe to use for certain file systems, but for code portability, POSITION(File) is the way to go.
In Clarion 6, the MDI attribute is no longer permitted on any toolbar window that is dockable (windows with the DOCK
and TOOLBOX attributes).
Next, compare the TXA's up through the first procedure (i.e., the program/global area). This might give you some ideas
regarding information converted from an old application that may not be compatible, or does not exist in Clarion 6.
6
Advanced Topics
Developers who modified the global error manager in their applications using DLLs will now need to be aware of the new
local error managers that are applied.
The linking of the ABC class support requires the following default pragmas settings to be included in the project system:
_ABCDllMode_=>0
_ABCLinkMode_=>1
Template support to automatically include these pragmas can be found in the Global Properties Classes tab by checking
the Enable the use of ABC Classes check box.
7
Advanced Programming Topics
The following topic displays the common EQUATES used by the Clarion IDE, as listed in the EQUATES.CLW and
TPLEQU.CLW files.
Template Equates
Event numbers - Field-specific
Event numbers - Field-independent
Standard Window Equates
! Event numbers
8
Advanced Topics
9
Advanced Programming Topics
! User-definable events
10
Advanced Topics
11
Advanced Programming Topics
INCLUDE 'property.clw'
COLOR:NONE EQUATE (-1)
COLOR:SCROLLBAR EQUATE (80000000H)
COLOR:BACKGROUND EQUATE (80000001H)
COLOR:ACTIVECAPTION EQUATE (80000002H)
COLOR:INACTIVECAPTION EQUATE (80000003H)
COLOR:MENU EQUATE (80000004H)
COLOR:WINDOW EQUATE (80000005H)
COLOR:WINDOWFRAME EQUATE (80000006H)
COLOR:MENUTEXT EQUATE (80000007H)
COLOR:WINDOWTEXT EQUATE (80000008H)
COLOR:CAPTIONTEXT EQUATE (80000009H)
COLOR:ACTIVEBORDER EQUATE (8000000AH)
COLOR:INACTIVEBORDER EQUATE (8000000BH)
COLOR:APPWORKSPACE EQUATE (8000000CH)
COLOR:HIGHLIGHT EQUATE (8000000DH)
COLOR:HIGHLIGHTTEXT EQUATE (8000000EH)
COLOR:BTNFACE EQUATE (8000000FH)
COLOR:BTNSHADOW EQUATE (80000010H)
COLOR:GRAYTEXT EQUATE (80000011H)
COLOR:BTNTEXT EQUATE (80000012H)
COLOR:INACTIVECAPTIONTEXT EQUATE (80000013H)
COLOR:BTNHIGHLIGHT EQUATE (80000014H)
12
Advanced Topics
13
Advanced Programming Topics
FONT:Screen EQUATE(0)
FONT:Printer EQUATE(1)
FONT:Both EQUATE(2)
FONT:TrueTypeOnly EQUATE(4)
FONT:FixedPitchOnly EQUATE(8)
CHARSET:ANSI EQUATE ( 0)
CHARSET:DEFAULT EQUATE ( 1)
CHARSET:SYMBOL EQUATE ( 2)
CHARSET:MAC EQUATE ( 77)
CHARSET:SHIFTJIS EQUATE (128)
CHARSET:HANGEUL EQUATE (129)
CHARSET:JOHAB EQUATE (130)
CHARSET:GB2312 EQUATE (134)
CHARSET:CHINESEBIG5 EQUATE (136)
CHARSET:GREEK EQUATE (161)
CHARSET:TURKISH EQUATE (162)
CHARSET:HEBREW EQUATE (177)
CHARSET:ARABIC EQUATE (178)
CHARSET:BALTIC EQUATE (186)
CHARSET:CYRILLIC EQUATE (204)
14
Advanced Topics
! Types
OMIT('***',_WIDTH32_)
SIGNED EQUATE(SHORT)
UNSIGNED EQUATE(USHORT)
_nopos EQUATE(08000H)
15
Advanced Programming Topics
***
COMPILE('***',_WIDTH32_)
SIGNED EQUATE(LONG)
UNSIGNED EQUATE(LONG)
_nopos EQUATE(080000000H)
***
BOOL EQUATE(SIGNED)
ff_:NORMAL EQUATE(0)
ff_:READONLY EQUATE(1)
ff_:HIDDEN EQUATE(2)
ff_:SYSTEM EQUATE(4)
ff_:DIRECTORY EQUATE(10H)
ff_:ARCHIVE EQUATE(20H)
ff_:LFN EQUATE(80H)
ff_:queue QUEUE,PRE(ff_),TYPE
name string(13)
date long
time long
size long
attrib byte
END
FILE:MaxFileName EQUATE(256)
FILE:MaxFilePath EQUATE(260)
FILE:Queue QUEUE,PRE(FILE),TYPE
Name STRING(FILE:MaxFileName)
ShortName STRING(13)
Date LONG
Time LONG
Size LONG
16
Advanced Topics
Attrib BYTE
END
oleQ QUEUE,TYPE
name CSTRING(64)
clsid CSTRING(64)
progid CSTRING(64)
END
!FileDialog/FileDialogA equates
FILE:Save EQUATE(1)
FILE:KeepDir EQUATE(2)
FILE:NoError EQUATE(4)
FILE:Multi EQUATE(8)
FILE:LongName EQUATE(10H)
FILE:Directory EQUATE(20H)
FILE:CreatePrompt EQUATE(40H)
FILE:AddExtension EQUATE(80H)
OCX:default EQUATE(0)
OCX:16bit EQUATE(1)
OCX:32bit EQUATE(2)
OCX:1632bit EQUATE(3)
DOCK:Left EQUATE(1)
DOCK:Top EQUATE(2)
DOCK:Right EQUATE(4)
DOCK:Bottom EQUATE(8)
DOCK:Float EQUATE(16)
DOCK:All EQUATE(31)
TPSREADONLY EQUATE(1)
17
Advanced Programming Topics
Match:Soundex EQUATE(3)
Match:NoCase EQUATE(10H) ! May be added to Simple,Wild and Regular
18
Advanced Topics
ITEMIZE(1),PRE(DriverOp)
ADD EQUATE
BOF EQUATE
BUILDfile EQUATE
APPEND EQUATE
BUILDdyn EQUATE
BUILDkey EQUATE
CLOSE EQUATE
COMMIT EQUATE
COPY EQUATE
CREATE EQUATE
DELETE EQUATE
DUPLICATE EQUATE
EMPTY EQUATE
EOF EQUATE
GETfilekey EQUATE
GETfileptr EQUATE
GETkeyptr EQUATE
HOLD EQUATE
LOCK EQUATE(20)
LOGOUT EQUATE(22)
NAME EQUATE
NEXT EQUATE
OPEN EQUATE
PACK EQUATE
POINTERfile EQUATE
POINTERkey EQUATE
FLUSH EQUATE
PUT EQUATE
PREVIOUS EQUATE
RECORDSfile EQUATE
RECORDSkey EQUATE
BUILDdynfilter EQUATE
19
Advanced Programming Topics
RELEASE EQUATE(36)
REMOVE EQUATE
RENAME EQUATE
ROLLBACK EQUATE(40)
SETfile EQUATE
SETfilekey EQUATE
SETfileptr EQUATE
SETkey EQUATE
SETkeykey EQUATE
SETkeyptr EQUATE
SETkeykeyptr EQUATE
SHARE EQUATE
SKIP EQUATE
UNLOCK EQUATE
ADDlen EQUATE
BYTES EQUATE
GETfileptrlen EQUATE
PUTfileptr EQUATE
PUTfileptrlen EQUATE
STREAM EQUATE
DUPLICATEkey EQUATE
WATCH EQUATE
APPENDlen EQUATE
SEND EQUATE
POSITIONfile EQUATE
POSITIONkey EQUATE
RESETfile EQUATE
RESETkey EQUATE
NOMEMO EQUATE
REGETfile EQUATE
REGETkey EQUATE
NULL EQUATE
SETNULL EQUATE
SETNONNULL EQUATE
SETproperty EQUATE
GETproperty EQUATE
GETblobdata EQUATE(75)
PUTblobdata EQUATE
BLOBSIZE EQUATE
SETblobproperty EQUATE
GETblobproperty EQUATE
20
Advanced Topics
BUFFER EQUATE
SETviewfields EQUATE
CLEARfile EQUATE
RESETviewfile EQUATE
BUILDevent EQUATE
SETkeyproperty EQUATE
GETkeyproperty EQUATE
DOproperty EQUATE(88)
DOkeyproperty EQUATE
DOblobproperty EQUATE
VIEWSTART EQUATE(92)
VIEWSTOP EQUATE
GETNULLS EQUATE(96)
S EQUATE
GETSTATE EQUATE
RESTORESTATE EQUATE
CALLBACK EQUATE
FREESTATE EQUATE(102)
DESTROY EQUATE(104)
END
ITEMIZE(1),PRE(DataType)
BYTE EQUATE
SHORT EQUATE
USHORT EQUATE
DATE EQUATE
TIME EQUATE
LONG EQUATE
ULONG EQUATE
SREAL EQUATE
REAL EQUATE
DECIMAL EQUATE
PDECIMAL EQUATE
BFLOAT4 EQUATE(13)
BFLOAT8 EQUATE
STRING EQUATE(18)
CSTRING EQUATE
PSTRING EQUATE
MEMO EQUATE
21
Advanced Programming Topics
BLOB EQUATE(27)
END
REG_CLASSES_ROOT EQUATE(80000000h)
REG_CURRENT_USER EQUATE(80000001h)
REG_LOCAL_MACHINE EQUATE(80000002h)
REG_USERS EQUATE(80000003h)
REG_PERFORMANCE_DATA EQUATE(80000004h)
REG_CURRENT_CONFIG EQUATE(80000005h)
REG_DYN_DATA EQUATE(80000006h)
Template Equates
! Template Equates
22
Advanced Topics
TBarBrwFirst EQUATE(2000)
TBarBrwInsert EQUATE(TBarBrwFirst)
TBarBrwChange EQUATE(TBarBrwFirst+1)
TBarBrwDelete EQUATE(TBarBrwFirst+2)
TBarBrwSelect EQUATE(TBarBrwFirst+3)
TBarBrwBottom EQUATE(TBarBrwFirst+4)
TBarBrwTop EQUATE(TBarBrwFirst+5)
TBarBrwPageDown EQUATE(TBarBrwFirst+6)
TBarBrwPageUp EQUATE(TBarBrwFirst+7)
TBarBrwDown EQUATE(TBarBrwFirst+8)
TBarBrwUp EQUATE(TBarBrwFirst+9)
TBarBrwLocate EQUATE(TBarBrwFirst+10)
23
Advanced Programming Topics
TBarBrwHistory EQUATE(TBarBrwFirst+11)
TBarBrwHelp EQUATE(TBarBrwFirst+12)
TBarBrwLast EQUATE(TBarBrwHelp)
VCRForward EQUATE(TBarBrwDown)
VCRBackward EQUATE(TBarBrwUp)
VCRPageForward EQUATE(TBarBrwPageDown)
VCRPageBackward EQUATE(TBarBrwPageUp)
VCRFirst EQUATE(TBarBrwTop)
VCRLast EQUATE(TBarBrwBottom)
VCRInsert EQUATE(TBarBrwInsert)
VCRNone EQUATE(0)
FormMode EQUATE(1)
BrowseMode EQUATE(2)
TreeMode EQUATE(3)
!Resize strategies
!Positional Strategies
!Horizontal
Resize:LockXPos EQUATE(0000000000000001b) !locks XPos of control
24
Advanced Topics
Resize:FixNearestX EQUATE(0000000000010000b)
!Vertical
Resize:LockYPos EQUATE(0000000100000000b) !locks YPos of control
!Template Warnings
25
Advanced Programming Topics
26
Advanced Topics
&'FREFUTGARGIBGOLGOSGREGUTHAMHEM'|
&'HOBHOTINGJASJONKAGKEAKIRKORKYO'|
&'LATLEOLIGLOUMACMAQMARMAUMCKMER'|
&'MILMONMORNATNOLOKEPAGPAUPETPIN'|
&'PORPULRAUREYROBROSRUBSALSCASCH'|
&'SCRSHASIGSKISNASOUSTESTISUNTAY'|
&'TIRTUCVANWACWASWEIWIEWIMWOLYOR')
ScrollSort:AllowAlpha EQUATE(1)
ScrollSort:AllowAlt EQUATE(2)
ScrollSort:AllowNumeric EQUATE(4)
ScrollSort:CaseSensitive EQUATE(8)
SortRequest:SelectSort EQUATE(1)
SortRequest:Reset EQUATE(2)
SortRequest:LocateRecord EQUATE(3)
SortResult:Changed EQUATE(1)
SortResult:OK EQUATE(2)
LocateOnPosition EQUATE(1)
LocateOnValue EQUATE(2)
LocateOnEdit EQUATE(3)
FillBackward EQUATE(1)
FillForward EQUATE(2)
RefreshOnPosition EQUATE(1)
RefreshOnQueue EQUATE(2)
RefreshOnTop EQUATE(3)
RefreshOnBottom EQUATE(4)
RefreshOnCurrent EQUATE(5)
27
Advanced Programming Topics
28
Advanced Topics
INCLUDE('KEYCODES.CLW')
This file contains EQUATE statements for most of the keycodes supported by Windows. These keycode EQUATEs are
used for greater code readability wherever you need to set or compare keyboard input.
29
Advanced Programming Topics
30
Advanced Topics
31
Advanced Programming Topics
32
Advanced Topics
33
Advanced Programming Topics
34
Advanced Topics
35
Advanced Programming Topics
36
Advanced Topics
37
Advanced Programming Topics
38
Advanced Topics
39
Advanced Programming Topics
40
Advanced Topics
41
Advanced Programming Topics
42
Advanced Topics
43
Advanced Programming Topics
44
Advanced Topics
45
Advanced Programming Topics
Dictionary Class
The new Clarion threading model dictates that the existing File and Relation Managers use threaded objects (i.e. a new
instance on every thread).
One of the effects of this is that the traditional ABC code that initializes both File and Relation Managers (contained in the
DctInit generated procedure) now has to be executed whenever a new thread is started. Likewise, the Managers’ kill code
(traditionally contained in DctKill) must be called whenever a thread is terminated.
To facilitate this, a small globally defined class called Dictionary will be generated into every ABC template based
application that does not have its global data defined external to the application. (i.e. the File and Relation managers
compiled locally). The Dictionary object contains only construct and destruct methods but, more important, it is a threaded
object.
Example:
Dictionary CLASS,THREAD
Construct PROCEDURE
Destruct PROCEDURE
END
Dictionary.Construct PROCEDURE
CODE
DctInit()
Dictionary.Destruct PROCEDURE
CODE
DctKill()
This means that the Construct method will be called whenever a new thread comes into existence and the Destruct
method will be called whenever a thread is terminated. The constructor calls DctInit and the destructor calls DctKill.
Therefore, DctInit is called whenever a thread is started and DctKill is now called whenever a thread is terminated; thus
ensuring that threaded File and Relation managers are created and destroyed correctly.
46
Advanced Topics
The FileCallBackInterface methods are called before and after each file operation. For every FileCallBackInterface
registered with a FILE or VIEW the FileCallBackInterface.FunctionCalled method is called before the operation
(opcode) is performed. If any of these methods return FALSE or Parameters.StopOperation is set to TRUE, then the
operation is not executed. After the operation has been called, or would have been called if
FileCallBackInterface.FunctionCalled did not return FALSE, FileCallBackInterface.FunctionDone is called for every
registered interface (See CALLBACK).
Both of the FileCallBackInterface methods receive an opCode and Parameters. Parameters are set up to offer different
values depending on the particular opCode passed to the method.
The EQUATES.CLW file found in LIBSRC defines valid opCodes using the following format:
DriverOp:Action
47
Advanced Programming Topics
GetProperty x = file{property,index}
48
Advanced Topics
49
Advanced Programming Topics
50
Advanced Topics
51
Advanced Programming Topics
VIEWSTART and VIEWEND are called by the ISAM View Engine when it executes NEXT, PREV, POSITIONfile,
REGETfile, RESETfile, GETfileptr, RECORDSfile,and SKIP.
The runtime library will call the file driver once when starting a transaction statement (LOGOUT, COMMIT, or ROLLBACK)
with an opCode of DriverOps:STARTTRAN and once when ending the transaction statement with an opCode of
DriverOps:ENDTRAN. If a callback has been registered with the first file in the logout file list, then it will be called with
opCode set to DriverOps:STARTTRAN at the start of the transaction command and DriverOps:ENDTRAN at the end of
the transaction command. In this case Parameters.TransactionOpCode will be set to either DriverOps:LOGOUT,
DriverOps:COMMIT or DriverOps:ROLLBACK.
52
Advanced Topics
INCLUDE(‘CLIB.CLW’)
This file contains Clarion prototypes for various string handling functions, integer math, character type functions, and low
level file manipulation functions. Refer to your C/C++ Library Reference for more information on individual functions.
INCLUDE(‘WINAPI.CLW’,’Equates’)
Include the Prototypes section of WINAPI.CLW in the "Inside the Global Map" embed point:
INCLUDE(‘WINAPI.CLW’,’Prototypes’)
Refer to your Windows API reference for more information on the individual API functions available to you in categories
such as:
Creating Windows
Window Support
Message Processing
Memory Management
Bitmaps and Icons
Color Palette Control
Sound
Character Sets and Strings
Communications
Metafiles
Tool Help Library
File Compression
53
Advanced Programming Topics
C/C++ to Clarion
54
Advanced Topics
COLORREF
C++: typedef unsigned long COLORREF;
Modula-2: TYPE COLORREF = LONGINT;
Run-Time Variables
The following list of procedures are those internal Clarion procedures that are ‘safe’ to call at run-time. Unless otherwise
stated, assume that these procedures have been given external C linkage.
Cla$ACOS The Clarion ACOS() procedure. Returns the inverse cosine of the val parameter.
C++: double Cla$ACOS(double val)
Modula-2: Cla$ACOS(val :LONGREAL):LONGREAL;
val: A numeric expression describing an angle in radians.
Cla$ARC The Clarion ARC statement. Places an arc of an ellipse on the current window or report, bounded by
the rectangle defined by the x, y, wd and ht parameters.
C++: void Cla$ARC(int x, int y, int wd, int ht, int start, int end)
Modula-2: Cla$ARC(x,y,wd,ht,start,end: INTEGER);
x: An integer specifying the horizontal position of the starting point.
y: An integer specifying the vertical position of the starting point.
wd: An integer specifying then width.
ht: An integer specifying then height.
start: An integer specifying the start of the arc in 10th’s of a degree.
end: An integer specifying the end of the arc in 10th’s of a degree.
55
Advanced Programming Topics
Cla$ASIN The Clarion ASIN() procedure. Returns the inverse sine of the val parameter.
C++: double Cla$ASIN(double val)
Modula-2: Cla$ASIN(val LONGREAL): LONGREAL;
val: A numeric expression describing an angle in radians.
Cla$ATAN The Clarion ATAN() procedure. Returns the inverse tangent of the val parameter.
C++: double Cla$ATAN(double val)
Modula-2: Cla$ATAN(val: LONGREAL):LONGREAL;
val: A numeric expression describing an angle in radians.
Cla$BOX The Clarion BOX statement. This procedure draws a box of the color specified by the COLORREF
structure, starting at position x, y of the width and height specified on the current window or report.
C++: void Cla$BOX(int x, int y, int wd, int ht, COLORREF fillcolor)
Modula-2: Cla$BOX(x, y, wd, ht: INTEGER; fillcolor: COLORREF);
x: An integer specifying the horizontal start position.
y: An integer specifying the vertical start position.
wd: An integer specifying the width.
ht: An integer specifying the height.
fillcolor: A COLORREF structure.
Cla$BSHIFT The Clarion BSHIFT() procedure. This procedure returns the result of bit shifting val by count binary
positions. If count is positive, val is shifted left, if count is negative val is shifted right.
C++: long Cla$BSHIFT(long val, int count)
Modula-2: Cla$BSHIFT(val: LONGINT; count: INTEGER): LONGINT;
val: A numeric expression.
count: A numeric expression.
Cla$CHORD The Clarion CHORD statement. Draws a closed sector ellipse on the current window or report inside
the box specified by the x, y, wd and ht parameters and in the color provided in the COLORREF
structure. The start and end parameters specify which part of the ellipse to draw.
C++: void Cla$CHORD(int x, int y, int wd, int ht, int start, int end, COLORREF fillcolor)
Modula-2: Cla$CHORD(x, y, wd, ht, start, end: INTEGER; fillcolor: COLORREF);
x: An integer specifying the horizontal start position.
y: An integer specifying the vertical start position.
wd: An integer specifying the width.
ht: An integer specifying the height.
start: An integer expressing the string of the chord in 10th’s of a degree.
end: An integer expressing the end of the chord in 10th’s of a degree.
fillcolor: A COLORREF structure.
56
Advanced Topics
Cla$CLOCK The Clarion CLOCK() procedure. Returns the system time in the form of a Clarion standard time.
C++: long Cla$CLOCK(void)
Modula-2: Cla$CLOCK(): LONGINT;
Cla$COS The Clarion COS() procedure. Returns the cosine of the val parameter.
C++: double Cla$COS(double val)
Modula-2: Cla$COS(val: LONGREAL): LONGREAL;
val: A numeric expression describing an angle in radians.
Cla$DATE The Clarion DATE() procedure. Returns a Clarion standard date value form the component day,
month and year parameters.
C++: long Cla$DATE(unsigned mn, unsigned dy, unsigned yr)
Modula-2: Cla$DATE(mn, dy, yr: CARDINAL): LONGINT;
mn: A numeric expression for the month in the range 1 to 12.
dy: A numeric expression for the day in the range 1 to 31.
yr A numeric expression for the year in the range 1801 to 2099.
Cla$DAY The Clarion DAY() procedure. Returns the day in the range 1 to 31 from the Clarion standard date
parameter.
C++: long Cla$DAY(long dt)
Modula-2: Cla$DAY(dt: LONGINT): LONGINT;
dt: A numeric expression for Clarion standard date.
Cla$ELLIPSE The Clarion ELLIPSE statement. Draws an ellipse on the current window or report, of the color
specified in the COLORREF structure, inside the area bounded by the x, y, wd and ht
parameters.
C++: void Cla$ELLIPSE(int x, int y, int wd, int ht, COLORREF fillcolor)
Modula-2: Cla$ELLIPSE(x, y, wd, ht: INTEGER; fillcolor: COLOREF);
x: An integer expression.
y: An integer expression.
wd: An integer expression.
ht: An integer expression.
fillcolor: A COLORREF structure.
Cla$INT The Clarion INT() procedure. Returns the integer portion of the val parameter. The value is truncated at
the decimal point and no rounding is performed.
C++: double Cla$INT(double val)
Modula-2: Cla$INT(val: LONGREAL): LONGREAL;
val: A numeric expression.
57
Advanced Programming Topics
Cla$LOG10 The Clarion LOG10() procedure. Returns the base 10 logarithm of the val parameter.
C++: double Cla$LOG10(double val)
Modula-2: Cla$LOG10(val: LONGREAL): LONGREAL;
val: A numeric expression.
Cla$LOGE The Clarion LOGE() procedure. Returns the natural logarithm of the val parameter.
C++: double Cla$LOGE(double val)
Modula-2: Cla$LOGE(val: LONGREAL): LONGREAL;
val: A numeric expression.
Cla$MONTH The Clarion MONTH() procedure. Returns the month from a Clarion standard date in the range 1 to
12.
C++: long Cla$MONTH(long dt)
Modula-2: Cla$MONTH(dt: LONGINT): LONGINT;
dt: A numeric expression containing a Clarion standard date.
Cla$MOUSEX The Clarion MOUSEX() procedure. Returns the horizontal position of the mouse.
C++: int Cla$MOUSEX(void)
Modula-2: Cla$MOUSEX(): INTEGER;
Cla$MOUSEY The Clarion MOUSEY() procedure. Returns the horizontal position of the mouse.
C++: int Cla$MOUSEY(void)
Modula-2: Cla$MOUSEY(): INTEGER;
Cla$NUMERIC The Clarion NUMERIC() procedure. Returns 1 (true) if str contains a valid representation of a
number, otherwise returns 0 (false).
C++: unsigned Cla$NUMERIC(char *str, unsigned slen)
Modula-2: Cla$NUMERIC(VAR str: ARRAY OF CHAR; slen:CARDINAL): CARDINAL;
str: A pointer to a string.
slen: Length of the str parameter.
Cla$RANDOM The Clarion RANDOM() procedure. Returns a pseudo-random number who’s value will be
between the low and high bound values.
C++: long Cla$RANDOM(long low, long high)
Modula-2: Cla$RANDOM(low, high: LONGINT): LONGINT;
low: A numeric value specifying the lower bound.
high: A numeric value specifying the upper bound.
58
Advanced Topics
Cla$ROUND The Clarion ROUND() procedure. Returns the val parameter rounded to power of 10 specified by
the ord parameter.
C++: double Cla$ROUND(double val, double ord)
Modula-2: Cla$ROUND(val, ord: LONGREAL): LONGREAL;
val: A numeric expression.
ord: A numeric expression equal to a power of 10 (e.g. .001, .0, 1, 10, 100 etc...).
Cla$SETCLOCK
The Clarion SETCLOCK statement. Sets the system clock to the time contained in the dt
parameter.
C++: void Cla$SETCLOCK(long dt)
Modula-2: Cla$SETCLOCK(dt: LONGINT);
dt: A numeric expression representing a Clarion standard time.
Cla$SETTODAY
The Clarion SETTODAY statement. Sets the DOS system date to that contained in the dt
parameter.
C++: void Cla$SETTODAY(long dt)
Modula-2: Cla&SETTODAY(dt: LONGINT);
dt: A numeric expression containing a Clarion standard date.
Cla$SIN The Clarion SIN() procedure. Returns the sine of the val parameter.
C++: double Cla$SIN(double val)
Modula-2: CLA$SIN(val: LONGREAL): LONGREAL;
val: A numeric expression describing an angle in radians.
Cla$SQRT The Clarion SQRT() procedure. Returns the square root of the val parameter.
C++: double Cla$SQRT(double val)
Modula-2: Cla$SQRT(val:LONGREAL): LONGREAL;
val: A numeric expression.
Cla$TAN The Clarion TAN() procedure. Returns the tangent of the val parameter.
C++: double Cla$TAN(double val)
Modula-2: Cla$TAN(val: LONGREAL): LONGREAL;
val: A numeric expression describing an angle in radians.
Cla$TODAY The Clarion TODAY() procedure. Returns the system date in Clarion standard date format.
C++: long Cla$TODAY(void)
Modula-2: Cla$TODAY(): LONGINT;
59
Advanced Programming Topics
Cla$YEAR The Clarion YEAR() procedure. Extracts the year from a Clarion standard date, in the range 1801 to
2099.
C++: long Cla$YEAR(long dt)
Modula-2: Cla$YEAR(dt: LONGINT): LONGINT;
dt: A numeric expression describing a Clarion standard date.
The following section describes the use Clarion internal run-time string handling procedures available to 3GL code.
Clarion uses a LISP like approach to string handling whereby, parameters are pushed onto the top of the string stack, with
operations being performed on the topmost entries. Assume, unless otherwise documented, that the procedures remove
(or Pop) items off the stack that they have used.
Please note that some of the following procedures require pointers to null terminated strings, to be passed as parameters.
Modula-2 programmers should use the Modula library procedure Str.StrToC to convert strings to null terminated
equivalents. Also, the pragma call(o_a_size=>off,o_a_copy=>off) must be issued to prevent the passing of array size
information to the run-time procedures.
Cla$PopCString Takes the topmost item off the stack and copies it to the string pointed to by s; len contains the
length of the string copied to s.
C++: void Cla$PopCString(char *s, unsigned len)
Modula-2: Cla$PopCString(s: POINTER TO CHAR; len: CARDINAL);
s: A pointer to a null terminated string
len: The length of string s
Cla$PopPString Takes the topmost item off the stack and copies it to the string pointed to by s; len contains the
length of the string copied to s. The string is converted to a Pascal style string (i.e. first
byte is string length) during copy.
C++: void Cla$PopPString(char *s, unsigned len)
Modula-2: Cla$PopPString(VAR s: ARRAY OF CHAR; len: CARDINAL);
s: A pointer to a string
len: The length of string s
Cla$PopString Pops the uppermost stack item and copies it to the string s.
C++: void Cla$PopString(char *s, unsigned len)
Modula-2: Cla$PopString(VAR s: ARRAY OF CHAR; len: CARDINAL);
s: A pointer to a null terminated string
len: The length of string s
60
Advanced Topics
Cla$PushString Pushes the string s onto the top of the stack. Len specifies the length of string s.
C++: void Cla$PushString(char *s, unsigned len)
Modula-2: Cla$PushString(VAR s: ARRAY OF CHAR; len: CARDINAL);
s: A pointer to a string
len: The length of string s
Cla$StackALL The Clarion ALL() procedure. Pops the top item of the stack and replaces it by a string containing
the original string replicated as many times as necessary to produce a string of length
len.
C++: void Cla$StackALL(unsigned len)
Modula-2: Cla$StackALL(len: CARDINAL);
len: An unsigned integer
Cla$StackCENTER The Clarion CENTER() procedure. Pops the topmost item of the stack and replaces it with a
string padded with leading spaces so as to center the text in a string of length len.
C++: void Cla$StackCENTER(unsigned len)
Modula-2: Cla$StackCENTER(len: CARDINAL);
len: An unsigned integer
Cla$StackCLIP The Clarion CLIP() procedure. Removes trailing spaces from the top most item on the stack.
C++: void Cla$StackCLIP(void)
Modula-2: Cla$StackCLIP();
Cla$StackCompare Compares the top item on the stack (s1) with the 2nd item on the stack (s2) and returns one
of the following values:
-1: if s1 < s2
0: if s1 = s2
1: if s1 > s2
After the compare instruction, s1 and s2 are removed from the stack automatically.
C++: int Cla$StackCompare(void)
Modula-2: Cla$StackCompare(): INTEGER;
Cla$StackCompareN Compares the topmost item on the stack to null. Returns true if the topmost item is null,
otherwise returns false.
C++: int Cla$StackCompareN(void)
Modula-2: Cla$StackCompareN(): INTEGER;
61
Advanced Programming Topics
Cla$StackConcat Pops the top two items off the stack, concatenates them together and pushes the resulting
string back onto the stack.
C++: void Cla$StackConcat(void)
Modula-2: Cla$StackConcat();
Cla$StackINSTRING The Clarion INSTRING() procedure. Searches the topmost item on the stack, for any
occurrence of the second item on the stack. The search starts at character position start
and increments the start position by step until the end of the string is reach. Returns the
iteration count required to find the search string, or 0 if not found.
C++: unsigned Cla$StackINSTRING(unsigned step, unsigned start)
Modula-2: Cla$StackINSTRING(step, start: CARDINAL): CARDINAL;
step: An unsigned integer, the search increment
start: An unsigned integer, the start position of the search
Cla$StackLEFT The Clarion LEFT() procedure. Replaces the topmost string on the stack with its left justified
equivalent. The replacement sting will have a length of len.
C++: void Cla$StackLEFT(unsigned len)
Modula-2: Cla$StackLEFT(len: CARDINAL);
len: An unsigned integer
Cla$StackLen Returns the length of the topmost item on the stack. Does not pop the item off the stack.
C++: unsigned Cla$StackLen(void)
Modula-2: Cla$StackLen(): CARDINAL;
Cla$StackLen2 Returns the length of the topmost item on the stack. Pops the item of the stack after getting its
length.
C++: unsigned Cla$StackLen2(void)
Modula-2: Cla$StackLen2(): CARDINAL;
Cla$StackLOWER The Clarion LOWER() procedure. Replaces the topmost string on the stack with its lower case
equivalent.
C++ void Cla$StackLOWER(void)
Modula-2: Cla$StackLOWER();
62
Advanced Topics
Cla$StackNUMERIC Returns true if the topmost string on stack contains a valid numeric representation,
otherwise returns false.
C++: unsigned Cla$StackNUMERIC(void)
Modula-2: Cla$StackNUMERIC(): CARDINAL;
Cla$StackPRESS The Clarion PRESS statement. Pushes every character in the topmost string of the stack into
the Windows keyboard buffer.
C++: void Cla$StackPRESS(void)
Modula-2: Cla$StackPRESS();
Cla$StackRIGHT The Clarion RIGHT() procedure. Replaces the topmost item on the stack with its right justified
equivalent. The replacement string will have a length of len characters.
C++: void Cla$StackRIGHT(unsigned len)
Modula-2: Cla$StackRIGHT(len: CARDINAL);
len: An unsigned integer
Cla$StackSUB The Clarion SUB() procedure. Replaces the topmost string on the stack with a sub slice of the
string starting at character position pos and of length len.
C++: void Cla$StackSUB(unsigned pos, unsigned len)
Modula-2: Cla$StackSUB(pos, len: CARDINAL);
pos: An unsigned integer; the start position of the sub string
len: An unsigned integer; the length of the sub string
Cla$StackVAL The Clarion VAL() procedure. Returns the ANSI value of the first character of the topmost string
of the stack.
C++: unsigned char Cla$StackVAL(void)
Modula-2: Cla$StackVAL(): BYTE;
Cla$StackUPPER Replace the topmost string on the stack with its uppercase equivalent.
C++: void Cla$StackUPPER(void)
Modula-2: Cla$StackUPPER();
63
Advanced Programming Topics
Conversion Functions
Integer Math
Char Type Functions
Utility Functions
String Functions
Low-Level File Manipulation
The following functions comprise a sub-set of the standard SoftVelocity library that you can call from your Clarion, C/C++,
or Modula-2 code. All of these functions are fully documented in the SoftVelocity C Library Reference manual (or in any
ANSI-standard C library reference) and so, are not documented here. Unless otherwise indicated, assume that the
functions operate exactly as documented.
The purpose of this list is simply to let you know what C standard library functions are available and the correct prototypes
for each language.
Conversion Functions
Please note that some of the following functions require pointers to null terminated strings as parameters. Modula-2
programmers should use the Modula library procedure Str.StrToC to convert strings to null terminated equivalents. Also,
the pragma call(o_a_size=>off, o_a_copy=>off) must be issued to prevent the passing of array size information to the run-
time procedures.
64
Advanced Topics
Integer Math
The following functions have only been tested when implemented as functions. We do not advise defining _CT_MTF to
implement the functions as macros.
65
Advanced Programming Topics
Utility Functions
66
Advanced Topics
String Functions
strcat Concatenate two strings.
C++: char *strcat(char *_dest, const char *_source)
Modula-2: Not available
Clarion: StrCat(*cstring,*cstring),cstring,raw,name('_strcat')
strequ
C++: int strequ(const char *_s1, const char *_s2)
Modula-2: Not available
Clarion: StrEqu(*cstring,*cstring),short,raw,name('_strequ')
67
Advanced Programming Topics
68
Advanced Topics
69
Advanced Programming Topics
access checks whether the file (or directory) specified by the path parameter exists, and (if not a directory)
whether it can be accessed in the specified mode. The function returns 0 if access is permitted
using the specified mode, -1 if it fails the check. Mode of 0 checks for existence, mode of 2
checks for write permission, mode of 4 checks for read permission, and 6 checks for read/write.
Clarion: Access(*cstring,short),short,raw,name('_access')
70
Advanced Topics
rename Changes the name of the file or directory specified by the oldname parameter.
C++: int _rename(const char *_oldname, const char *_newname)
Modula-2: _rename(VAR _oldname, VAR _newname: ARRAY OF CHAR): INTEGER;
Clarion: API_Rename(*cstring, *cstring), short, raw,| name('_rename')
!Renamed to avoid conflict with Builtins.Clw
fnmerge Builds a complete path name from its component parts -- drive, directory, filename, and extension.
C++: void _fnmerge(char *_path, const char *_drive, const char *_dir, const char *_name, const char *_ext)
Modula-2: _fnmerge(VAR _path, VAR _drive, VAR _dir, VAR _name, VAR _ext: ARRAY OF CHAR);
Clarion: FnMerge(*cstring, *cstring, *cstring, *cstring,| *cstring), raw, name('_fnmerge')
Note: fnmerge is actually called using PathMerge in the prototypes in CWUTIL.INC
fnsplit This function breaks a complete path name into its component parts -- drive, directory, filename, and
extension.
C++: int _fnsplit(const char *_path, char *_drive, char *_dir, char *_name, char *_ext)
Modula-2: _fnsplit(VAR_path,VAR _drive,VAR _dir,VAR _name,VAR _ext:ARRAY OF CHAR):INTEGER;
Clarion: FnSplit(*cstring, *cstring, *cstring, *cstring,| *cstring), short, raw, name('_fnsplit')
Note: fnsplit is actually called using PathSplit using the prototypes in CWUTIL.INC.
mkdir Creates a new directory with the name passed in the path parameter.
C++: int _mkdir(const char *_path)
Modula-2: _mkdir(VAR _path: ARRAY OF CHAR): INTEGER;
Clarion: MkDir(*cstring),short,raw,name('_mkdir')
Equivalent to CreateDirectory
71
Advanced Programming Topics
72
Advanced Topics
These utilities are included as source in the Clarion \LIBSRC folder. To add the prototypes described below to your
existing applications, you need only include the CWUTIL.INC file in the Global Map section of your program:
INCLUDE('CWUTIL.INC'),ONCE
Each utility is described in more detail in its own separate help topic. Use the links below to reference the following
utilities:
BeginUnique
BLOBToFile
ByteToHex
CreateDirectory
EndUnique
FileExists
FileToBLOB
FullDrag
GetFileDate
GetFileTime
GetReg
GetTempFileName
GetTempPath
GetUserName
IsTermServer
LongToHex
OSVersion
PutReg
RemoveDirectory
ShortToHex
ValidateOLE
WindowExists
73
Advanced Programming Topics
BeginUnique( applicationname )
applicationname A string constant or variable that specifies name of your application. Example:
‘INVOICE.EXE’
BeginUnique returns FALSE if the program specified in applicationname is already running (active). If not running,
BeginUnique returns an event number specified by Windows. This event number can be used by the EndUnique
statement to terminate the single process mode.
To add this function to your existing applications, you need only include the CWUTIL.INC file in the Global Map section of
your program:
INCLUDE('CWUTIL.INC'),ONCE
See Also:
EndUnique
74
Advanced Topics
bloblabel The fully qualified label of the BLOB field. (Example: Customer.BlobImage)
filename A string constant or variable that names the output file where the BLOB will be
copied.
BLOBTOFILE is used to copy the contents of a BLOB to an external file. If the copy fails for any reason, BLOBTOFILE
returns the ERRORCODE posted.
BLOBTOFILE (and FILETOBLOB) are simply binary-to-binary operations.
If you need to save images to a BLOB, and later restore them to an output file, the type of image should also be saved in
the database (JPG, GIF, BMP, etc.). Using BLOBTOFILE to save to a different extension can produce unpredictable
results.
To add this function to your existing applications, you need only include the CWUTIL.INC file in the Global Map section of
your program:
INCLUDE('CWUTIL.INC'),ONCE
See Also:
FILETOBLOB
BLOB
75
Advanced Programming Topics
flag A BYTE used to designate a lower or upper case HEX symbol (A,B,C,D,E)
BYTETOHEX is used to convert a number to its Hexadecimal equivalent. If the flag variable is non-zero, any non-numeric
Hexadecimal symbols are returned in lowercase. If zero (default), the non-numeric digits are returned in uppercase.
To add this function to your existing applications, you need only include the CWUTIL.INC file in the Global Map section of
your program:
INCLUDE('CWUTIL.INC'),ONCE
76
Advanced Topics
CREATEDIRECTORY( directoryname )
CREATEDIRECTORY creates a new directory with the name passed in the directoryname parameter.
CREATEDIRECTORY returns zero (0) if successful, and non-zero if not. You can query the ERRNO built-in function to
trap for the following error codes:
3 – Path not found (One of the higher path components in directoryname)
5 – Access Denied (Possible Security Rights)
183 – Directory Already Exisits
To add this function to your existing applications, you need only include the CWUTIL.INC file in the Global Map section of
your program:
INCLUDE('CWUTIL.INC'),ONCE
77
Advanced Programming Topics
Example:
PROGRAM
GLO:NewDirectoryName STRING(200)
MAP
INCLUDE('CWUTIL.INC'),ONCE
MODULE('')
errno(),*SIGNED,NAME('__errno__') !prototype built-in error flag
END
END
CODE
GLO:NewDirectoryName = CLIP('C:\Temp')
IF CreateDirectory(GLO:NewDirectoryName)
CASE Errno()
OF 3
MESSAGE('Path Not Found')
OF 5
MESSAGE('Access Denied')
OF 183
MESSAGE('Directory Already Exists')
END
END
See Also:
RemoveDirectory
78
Advanced Topics
EndUnique( eventnumber )
EndUnique is used to invalidate the specified application event handle. This is useful where a function using BeginUnique
was no longer valid, and you need to override the single event process when subsequent applications are started.
To add support for this utility to your existing applications, you need only include the CWUTIL.INC file in the Global Map
section of your program:
INCLUDE('CWUTIL.INC'),ONCE
Example:
EndUnique(GLO:AppEventNumber)
See Also:
BeginUnique
79
Advanced Programming Topics
FILEEXISTS( filename )
filename A string constant or variable containing the name of the file (and path, if
applicable)
FILEEXISTS confirms the existence of a file. If FILEEXISTS returns TRUE (1), the file exists. If FILEEXISTS returns
FALSE (0), the file specified in the filename parameter does not exist.
To add support for this utility to your existing applications, you need only include the CWUTIL.INC file in the Global Map
section of your program:
INCLUDE('CWUTIL.INC'),ONCE
80
Advanced Topics
filename A string constant or variable that names the input file to copy to a BLOB field.
bloblabel The fully qualified label of the BLOB field. (Example: Customer.BlobImage)
FILETOBLOB is used to copy the contents of a file to a BLOB field. If the copy was unsuccessful, FILETOBLOB returns
the ERRORCODE posted.
To add support for this utility to your existing applications, you need only include the CWUTIL.INC file in the Global Map
section of your program:
INCLUDE('CWUTIL.INC'),ONCE
See Also:
BLOBTOFILE
BLOB
81
Advanced Programming Topics
FULLDRAG returns the current window drag setting. If the optional setdragflag is set to TRUE (1), full window dragging is
enabled. If the optional setdragflag is set to FALSE (0), full window dragging is disabled and only the window frame will
appear when dragging a window.
To add support for this utility to your existing applications, you need only include the CWUTIL.INC file in the Global Map
section of your program:
INCLUDE('CWUTIL.INC'),ONCE
82
Advanced Topics
GETFILEDATE( filename )
filename A string constant or variable containing the name of the file (and path, if
applicable)
GETFILEDATE returns the date stamp of the file specified by the filename parameter. The date is returned as a LONG
that is deformatted and returned in an @D2 picture format. If the file is invalid or does not exist, GETFILEDATE returns a
zero (0).
To add support for this utility to your existing applications, you need only include the CWUTIL.INC file in the Global Map
section of your program:
INCLUDE('CWUTIL.INC'),ONCE
83
Advanced Programming Topics
GETFILETIME( filename )
filename A string constant or variable containing the name of the file (and path, if
applicable)
GETFILETIME returns the time stamp of the file specified by the filename parameter. The time is returned as a LONG that
is deformatted and returned in an @T4 picture format. If the file is invalid or does not exist, GETFILETIME returns a zero
(0).
To add support for this utility to your existing applications, you need only include the CWUTIL.INC file in the Global Map
section of your program:
INCLUDE('CWUTIL.INC'),ONCE
84
Advanced Topics
GETREG Gets the value of a specific key and/or value from the system registry.
Root A LONG integer, variable or expression that contains the root section of the
registry from which to obtain the value. Valid values for this are defined in
equates.clw and are as follows:
REG_CLASSES_ROOT
REG_CURRENT_USER
REG_LOCAL_MACHINE
REG_USERS
REG_PERFORMANCE_DATA
REG_CURRENT_CONFIG
REG_DYN_DATA
keyname A STRING constant, variable or expression that contains the key name of the
key whose value is to be queried. This may contain a path separated by
backslash ‘\’ characters.
valuename A STRING constant, variable or expression that contains the name of the value
to be queried, if omitted, the value associated directly with the key is returned.
valuetype An optional integer variable that receives the type of value. It can be used for
correct interpreting of the returned valuename.
85
Advanced Programming Topics
The GETREG function returns the value of named entry in the system registry as a Clarion string. If the requested entry
does not exist, an empty string is returned.
Vista and Clarion Built-in Registry Functions
On Vista, a (non-elevated) app can READ the registry key HKLM (local machine) with no problems, but as far as writing to
the Registry it needs to use HKCU (current user).
If you code sign your executable and run with elevated priveleges then you can write to HKLM
Example:
PROGRAM
MAP
END
INCLUDE('EQUATES')
ColorScheme CSTRING(100)
CODE
ColorScheme =|
GETREG(REG_CURRENT_USER,'Control Panel\Current','Color Schemes')
!get the current user's color scheme
See Also:
PUTREG, DELETEREG
86
Advanced Topics
prefix A string constant or variable naming the prefix (first three letters) of the
temporary file. If blank, the default prefix used is ‘$$$’
pathname A string constant or variable naming the location of the temporary file. If
omitted, the system TEMP or TMP directory path is used.
GETTEMPFILENAME is used to generate a temporary file. If the pathname specified is invalid, GETTEMPFILENAME
returns an empty string.
Make sure to remove your temporary files that you create after use. The Windows system will not automatically remove
these files.
To add support for this utility to your existing applications, you need only include the CWUTIL.INC file in the Global Map
section of your program:
INCLUDE('CWUTIL.INC'),ONCE
87
Advanced Programming Topics
GETTEMPPATH( )
GETTEMPPATH Returns the name of the path specified by the Windows Environment variables
GETTEMPPATH is used to return the full path designated by the TMP or TEMP Windows Environment
settings.GETTEMPPATH returns the first Environment setting it finds.
To add support for this utility to your existing applications, you need only include the CWUTIL.INC file in the Global Map
section of your program:
INCLUDE('CWUTIL.INC'),ONCE
88
Advanced Topics
GETUSERNAME( )
GETUSERNAME is used to retrieve the current default user name, or the user name used to establish a network
connection. GETUSERNAME returns a blank string if an error is encountered.
To add support for this utility to your existing applications, you need only include the CWUTIL.INC file in the Global Map
section of your program:
INCLUDE('CWUTIL.INC'),ONCE
89
Advanced Programming Topics
ISTERMSERVER( )
It is a good practice for applications to detect whether they are running in a Terminal Services Client session in order to
optimize performance. For example, when an application is running on a remote session, it should eliminate unnecessary
graphic effects. If a user is running the application directly on the terminal, it is not necessary for the application to
optimize its behavior.
ISTERMSERVER is used to detect Terminal Server usage by returning the status of the System Metrics
SM_REMOTESESSION flag. ISTERMSERVER returns TRUE if an application is running in a Terminal Services Client
session, and FALSE if the application is running on the console.
This function is only valid for Windows 2000 or later.
To add support for this utility to your existing applications, you need only include the CWUTIL.INC file in the Global Map
section of your program:
INCLUDE('CWUTIL.INC'),ONCE
90
Advanced Topics
flag A BYTE used to designate a lower or upper case HEX symbol (A,B,C,D,E)
LONGTOHEX is used to convert a number to its Hexadecimal equivalent. If the flag variable is non-zero, any non-numeric
Hexadecimal symbols are returned in lowercase. If zero (default), the non-numeric digits are returned in uppercase.
To add support for this utility to your existing applications, you need only include the CWUTIL.INC file in the Global Map
section of your program:
INCLUDE('CWUTIL.INC'),ONCE
91
Advanced Programming Topics
PROP:WindowsVersion
Returns the string that describes Windows version running the program.
The runtime library returns the following information:
Windows/Win32 <version> [<category> | <edition>] [ServicePack] Build Number
Example:
GLO:WindowsVersion = SYSTEM{PROP:WindowsVersion}
92
Advanced Topics
root A LONG integer constant, variable or expression that specifies the root section of
the registry to which to write the value. Valid values for this are defined in
equates.clw and are as follows:
REG_CLASSES_ROOT
REG_CURRENT_USER
REG_LOCAL_MACHINE
REG_USERS
REG_PERFORMANCE_DATA
REG_CURRENT_CONFIG
REG_DYN_DATA
keyname A STRING constant, variable or expression that contains the key name of the key
whose value is to be written. This may contain a path separated by backslash ‘\’
characters. The keyname cannot contain a leading backslash.
valuename A STRING constant, variable or expression that contains the name of the value
to be written.
value A STRING constant, variable or expression that contains the value to be written
to the registry in the position given. If omitted, an empty string is written to the
registry.
valuetype A LONG integer constant, variable or expression that specifies the how to store
passed value. If omitted, the default value is REG_SZ (see below).
93
Advanced Programming Topics
The PUTREG procedure places the value into a valuename that exists in the Windows registry. The key and value will be
created if it does not exist. If the write to the registry is successful, PUTREG returns 0. If the write to the registry is
unsuccessful, the result is a non-zero value.
94
Advanced Topics
Example:
PROGRAM
MAP.
INCLUDE('EQUATES')
ColorScheme CSTRING(100)
CODE
!writes the current user's color scheme to the registry
ColorScheme = 'Windows Standard’
IF PUTREG(REG_CURRENT_USER,'Control Panel\Current','Color Schemes',ColorScheme)
MESSAGE('Unable to set the color scheme') !post error if non-zero value returned
END
See Also:
GETREG, DELETEREG
95
Advanced Programming Topics
REMOVEDIRECTORY( directoryname )
REMOVEDIRECTORY removes an existing directory with the name passed in the directoryname parameter.
REMOVEDIRECTORY returns zero (0) if successful, and non-zero if not. You can query the ERRNO built-in library
function to trap for the following error codes:
3 – Path not found (One of the higher path components in directoryname)
5 – Access Denied (Path may refer to a file, root directory, or current directory)
To add support for this utility to your existing applications, you need only include the CWUTIL.INC file in the Global Map
section of your program:
INCLUDE('CWUTIL.INC'),ONCE
MODULE('')
errno(),*SIGNED,NAME('__errno__') !proptotype built-in error flag
END
IF REMOVEDIRECTORY(GLO:NewDirectoryName)
CASE Errno()
OF 3
MESSAGE('Path Not Found')
OF 5
MESSAGE('Access Denied')
END
END
See Also:
CreateDirectory
96
Advanced Topics
RESIZEIMAGE Resize a valid graphic file to fit inside a target IMAGE control
Xpos A SHORT constant or variable identifying the horizontal position of the top left
corner of the target IMAGE control in dialog units.
Ypos A SHORT constant or variable identifying the vertical position of the top left
corner of the target IMAGE control in dialog units.
Width A SHORT constant or variable identifying the width of the target IMAGE control in
dialog units.
Height A SHORT constant or variable identifying the height of the target IMAGE control
in dialog units.
Report A valid label of a REPORT structure. Indicates that the control to store the
resized image is contained in a REPORT target instead of a WINDOW
RESIZEIMAGE is used to resize the image to fit the original control size. If an image is larger than the target control, the
image will be reduced to fit the target control’s position parameters. If an image is smaller than the target control, the
image will be expanded to fit the target control’s position parameters.
To add support for this utility to your existing applications, you need only include the CWUTIL.INC file in the Global Map
section of your program:
INCLUDE('CWUTIL.INC'),ONCE
Example:
CASE ACCEPTED()
OF ?LookupFile
ThisWindow.Update
LOC:Filename = FileLookup9.Ask(0)
DISPLAY
IF LOC:Filename
?Image1{PROP:TEXT} = LOC:Filename !Move filename to image field
ResizeImage(?Image1,114,132,90,64) !Resize it
END
OF ?OK
ThisWindow.Update
IF SELF.Request = ViewRecord AND NOT SELF.BatchProcessing THEN
POST(EVENT:CloseWindow)
END
END
97
Advanced Programming Topics
flag A BYTE used to designate a lower or upper case HEX symbol (A,B,C,D,E)
SHORTTOHEX is used to convert a number to its Hexadecimal equivalent. If the flag variable is non-zero, any non-
numeric Hexadecimal symbols are returned in lowercase. If zero (default), the non-numeric digits are returned in
uppercase.
To add support for this utility to your existing applications, you need only include the CWUTIL.INC file in the Global Map
section of your program:
INCLUDE('CWUTIL.INC'),ONCE
98
Advanced Topics
VALIDATEOLE is used to verify that an OLE control has been created successfully. VALIDATEOLE returns TRUE if the
OLE control has been successfully created.
If not successful, VALIDATEOLE can optionally display a message box that describes why the OLE control could not be
created, provided that the OLEFilename parameter is passed, and then returns FALSE. Otherwise, VALIDATEOLE just
returns FALSE if only the OLEControl is designated.
To add support for this utility to your existing applications, you need only include the CWUTIL.INC file in the Global Map
section of your program:
INCLUDE('CWUTIL.INC'),ONCE
99
Advanced Programming Topics
WINDOWEXISTS(windowtitle )
windowtitle A string constant or variable that specifies the window name (the window's
title).
WINDOWEXISTS is used to verify that a valid window exists whose window name matches the window title.
If WINDOWEXISTS succeeds (has a valid handle), the return value is TRUE (1).
To add support for this utility to your existing applications, you need only include the CWUTIL.INC file in the Global Map
section of your program:
INCLUDE('CWUTIL.INC'),ONCE
100
Advanced Topics
Build System
The Clarion build system in version 7 and higher supports both the legacy (Clarion 6 and prior versions) project system,
plus MSBuild (Microsoft) project files. MSBuild project files are supported via thee custom MSBuild Tasks: CW, CWClean
and Redirection. The CWClean task is used to remove files created by the CW task. The CW task creates a temporary
legacy project file and passes that to the Clarion Project System. The Redirection task can be used in MSBuild projects to
find files using a redirection file.
After solglobals.pi has been included, the project system will then add cwglobals.pi. This file is intended to be used to
contain company wide project settings. For example:
#pragma define(_Company_=>Our Company Name)
Project Commands
#section
#section <name>
-- section commands
#else <name>
-- section commands
#else <name>
-- section commands
#endsection
A section starts a list of commands that are activated on a #activate command. You can have one or mutually exclusive
sections by specifying a #else command. If a #activate command is encountered and a mutually exclusive section had
previous been activated via a #activate command, then the previous command is deactivated without the need to call
#deactivate.
Only #set and #pragma commands are valid within a section.
101
Advanced Programming Topics
Example:
-- Contents of cwglobal.pi
#section release
#set outdir=release
#else debug
#pragma debug(line_num=>on)
#pragma check(stack=>on)
#pragma check(index=>on)
#set outdir=debug
#endsection
#section Profile
#set profiling = on
#endsection
#set profiling = off
#section Tracing
#set tracing = on
#pragma define(_TRACING_=>on)
#endsection
#set tracing = off
#activate
#activate <sectionname>
Activates a set of commands previously defined by the #section command.
If the section being activated is part of a list of mutually exclusive sections, and one of these sections is currently active,
then an implicit #deactivate othersection is called.
When an #activate command is encountered then all #set and #pragma statements contained within that section are
executed.
#deactivate
#deactivate <sectionname>
Reverts all commands that where activated by a previous call to #activate sectionname.
This command will set the pragmas and project sets back to the value they were before the matching #activate call was
made.
102
Advanced Topics
Obsolete Commands:
The following project commands are no longer supported:
Command Remarks
103
Advanced Programming Topics
MSBuild tasks
.cwproj File Format
The .cwproj file is a standard MSBuild file in a format that allows the IDE to provide a graphical interface on top of the file.
This file consists of properties and items that are passed to the CW and CWClean tasks.
The following shows the properties and items in the .cwproj file, and which task parameter they map to.
104
Advanced Topics
Which task is invoked is dependent on the build target. If a Build is being performed (the default target), then the CW Task
is invoked. If a Clean is being performed, then the CWClean Task is invoked. If a Rebuild is being performed, then both
the CWClean Task and CW Task are invoked.
CW Task
Invokes the Clarion project system which produces executables (.exe), dynamic-link libraries (.dll), or code libraries (.lib).
Parameters
The following table describes the parameters of the CW task.
Parameters Description
105
Advanced Programming Topics
106
Advanced Topics
107
Advanced Programming Topics
Element Description
Element Description
108
Advanced Topics
Element Description
109
Advanced Programming Topics
110
Advanced Topics
Element Description
111
Advanced Programming Topics
Redirection Task
Get the full path of a file using the redirection system.
Parameters
The following table describes the parameters of the Redirection task.
Parameters Description
112
Advanced Topics
113
Advanced Programming Topics
Switch Formats
Valid switch formats are as follows":
/id
Example: C:\Clarion7\BIN\clarionCL /?
-id
Example: C:\Clarion7\BIN\clarionCL -?
--id
Example: C:\Clarion7\BIN\clarionCL --?
Valid Switches:
Id Parameters Description
The -tr switch uses the redirection (*.red) file, so you do not
have to supply the full path if the template file can be found
using the redirection system:
ClarionCL -tr abado.tpl
114
Advanced Topics
2. Passing an invalid switch will cause ClarionCL to behave as though ClarionCL /? was called.
Examples:
ClarionCL –tr mytemplate.tpl
Error Codes
CLCE004 Not enough parameters where supplied to run the command. The
switch is ignored
115
Advanced Programming Topics
Warning Codes
CLCW001 A duplicate switch was passed, but the switch can only appear
once. Only the first occurrence of the switch is processed
CLCW002 Too many parameters were supplied for the command. All extra
parameters are ignored
116
Advanced Topics
Introduction
The Project System is integrated into the Clarion Environment. It is a powerful sequential language that combines the
functionality of a batch processor, a linker and an intelligent compile-and-link system.
The Project System gives you total control over the compile and link process for the simplest single .EXE project up to
the most complicated multiple .DLL project.
The primary benefits of using the Project System are automation, efficiency, and accuracy. With a single command,
you can remake your entire project, no matter how complicated, and you can be assured that the correct source and
objects are included in the compile and link processes, plus, the components that don’t need it, don’t get reprocessed.
In addition, you can make different versions of your project (release version, debug version, evaluation/demo version,
etc.) with the flip of a switch.
Here is a simple example of some project system language generated by the Clarion Application Generator:
#noedit
#system win
#model clarion dll
#pragma debug(vid=>full)
#compile QWKTU_RD.CLW— GENERATED
#compile QWKTU_RU.CLW— GENERATED
#compile QWKTU_SF.CLW— GENERATED
#compile QWKTUTOR.clw /define(GENERATED=>on)— GENERATED
#compile QWKTU001.clw /define(GENERATED=>on)— GENERATED
#compile QWKTU002.clw /define(GENERATED=>on)— GENERATED
#compile QWKTU003.clw /define(GENERATED=>on)— GENERATED
#pragma link(C%L%TPS%S%.LIB)— GENERATED
#link QWKTUTOR.EXE
117
Advanced Programming Topics
Language Components
Keywords start with a pound sign ( # ). In the example, each keyword begins on a new line for readability. This is not
required.
Comments start with a double hyphen ( -- ) and are terminated by a Carriage Return or Line Feed.
Macros are surrounded by percent signs ( % ). You may want to think of macros as variables—a value is substituted
whenever the project system encounters a macro name surrounded by percent signs ( % ). See Project System
Macros.
Keyword Parameters are everything else you see in the example. Parameters and their syntax are discussed with
each keyword.
The Project System recognizes the following keywords:
#noedit
The #noedit command can be placed at the top of a project file to prevent menu-editing from the SoftVelocity
environment. It has no effect in the Clarion environment.
118
Advanced Topics
Macros are special strings that indicate a variable substitution is required. You may find it useful to think of macros as
variables.
A sequence of characters enclosed by % characters indicates a macro name. The following characters are permitted in
macro names:
ABCDEFGHIJKLM
NOPQRSTUVWXYZ
abcdefghijklm
nopqrstuvwxyz
0123456789_
The trailing % may be omitted provided the character following the macro name is not one of the characters above.
Whenever a % delimited macro name is encountered, it is replaced either by the string associated with that macro, or
by an empty string if there is no associated string. Substitution strings are associated with a macro by using the #set
command.
Two adjacent % characters may be used when a % character is required in the substituted string. This double %
technique can be used to delay macro substitution. For example:
If a single % had been specified in the first #set command, the macro %mymac would have been expanded (to the
empty string) before defining the replacement text for the macro %echo. The double % results in the project system
executing:
#message Hello
#message World
#message ""
#message ""
119
Advanced Programming Topics
#set
#set macroname = string
The #set command associates a macro name with a string. Any previous setting for the given macro is lost. The macro
name in the #set command should not be delimited by % characters. The string should be enclosed in single quotes if it
contains embedded spaces or project system keywords.
For example:
%linkit
#expand <file-name>
The filename is subjected to redirection analysis, and the following macros are defined:
%cpath Is set to the fully expanded filename where the file would be created.
%opath Is set to the fully expanded filename where the file would be opened.
%ext Is set to the extension of the filename.
%tail Is set to the filename, less extension, drive and path.
%cdir Is set to the directory where the file would be created.
%odir Is set to the directory where the file would be opened for read (if the file does not exist %opath is set the same as
%cpath).
For example, suppose the redirection file has the line,
*.def : . ; c:\ts\include
and the file c:\ts\include\io.def exists, and the current directory is d:\test then,
#expand io.def
is equivalent to,
120
Advanced Topics
#split <filename>
The filename is split into its base and extension. The following macros are defined:
For example:
#split d:\name.exe
is equivalent to,
121
Advanced Programming Topics
%manual_export Set this macro on to indicate that the #link command should not construct a .LIB file when a DLL
is linked. If this macro is not specified, a .LIB file is created automatically from the corresponding
.EXP file if found (see Module Definition File below), or from the object files in the link list.
%model Set by the #model command to its first argument, and examined by the #link command.
%name Set by the #split command.
%obj Set to the object filename in a #compile command.
%odir Set by the #expand command.
%opath Set by the #expand command.
%pragmastring Will always expand to the current state of the #pragma settings - this is useful for debugging.
%prjname Set to the assumed name of the project - this is usually derived from the project filename, but with
the path and extension removed. Where UNNAMED.PR is being used, it is derived from main
source filename without source and extension.
%remake Used within declare_compiler macros to determine whether source/object dependencies require
a remake.
%remake_jpi Used within declare_compiler macros to determine whether source/object dependencies require
a remake. %remake_jpi should be used for object files created by SoftVelocity compilers, which
contain additional information.
%reply Set by the #prompt command.
%S Set by the #system command to 32 indicating the instruction set being used to build the project.
The #link command uses this to derive the name of any required library files.
%src Set to the source filename in a #compile command.
%system Set by the #system command to its first argument, and examined by the #model and #link
commands.
%tail Set by the #expand command.
%tsc Set to on if a C or C++ source file is compiled.
%tscpp Set to on if a C++ source file is compiled.
%tsm2 Set to on if a Modula-2 source file is compiled.
%tspas Set to on if a Pascal source file is compiled.
%warnings Count of warnings produced by preceding compile or #file adderrors command.
%X The #link command uses this to derive the name of any required library files. It adds the letter ‘X’
to the library name, indicating a 32-bit link.
The above macros are examined by the #link command to determine which libraries to include, and then set to off.
122
Advanced Topics
Compile and link options are specified in a project file by means of the #system, #model and #pragma commands.
#system
#system operating_system [ target_type ]
The #system command is used to specify the target operating system and file type. The macros %system and
%filetype are set to the first and second arguments. See Special Project System Macros below.
The first argument specifies the target operating system, and may be win or win32.
The second argument indicates the target file type, and may be exe, lib, or dll. If omitted, exe is assumed.
The #system command affects the behavior of subsequent #model and #link commands. Therefore a #system
command must be specified before either of these. If more than one #system command occurs in a project, each must
be followed by a #model command in order to take effect.
#model
#model memory_model [ linking_convention ]
The #model command is used to specify the memory model to be used for subsequent compiles and links. This
memory model will continue to be used until modified by explicit #pragmas, or by another #model command.
The #model command sets the macros %model and %jpicall to its first and second parameters respectively. For
example,
is equivalent to
The first argument specifies the memory model, which is always ‘clarion’ for Clarion projects. The second indicates the
linking convention, which may be dll, lib, or customdll. If omitted, dll is assumed.
Setting the second argument to dll indicates that you will be creating an exe or dll that calls the standard Clarion dlls.
Setting the second parameter to lib indicates that you will be creating an exe, lib or dll that includes all the components
of the Standard Clarion libraries (and file drivers) in the exe, lib or dll. Using customdll indicates that you are linking to a
dll that was previously created with the lib link convention, so the standard Clarion dlls are not linked.
The #system command must be specified before the first #model command.
123
Advanced Programming Topics
#pragma
#pragma <#pragma> { , <#pragma> }
The #pragma command modifies the state of the #pragma options which affect the behavior of the SoftVelocity
compilers or linker. The syntax and meaning of all #pragmas are discussed under the SoftVelocity #pragmas section
below.
The special macro %pragmastring expands to the current state of all #pragma options which are not in their default
state - this can be useful for determining exactly which options are being used for a given compile. For example:
#message ‘%pragmastring’
#compile
#compile<source> [ #to <object> ] [ / <#pragma> { , <#pragma> } ]
{ , <source> [ #to <object> ] [ / <#pragma> { , <#pragma> } ] }
The #compile command causes each nominated source file to be compiled (if necessary). The name of the object file
may be specified using #to. If this is omitted, the name is derived from the source filename, with the extension .obj.
Any #pragmas specified in a #compile command apply only to the single source filename that precedes the / character.
The macro %make is set to on if a compile is necessary, off otherwise. The macros %src and %obj are set to the
names of the source and object filenames.
Each object file is added to the link list, i.e. there is an implicit:
#pragma link( %obj )
For example:
#compile fred.c #to fred.obj
#compile george.cpp /debug(vid=>full)
It is possible to reconfigure the behavior of the Project System when compiling source files of a given extension using
the #declare_compiler command. This may also be used to declare actions to perform for different file extensions - for
example, to support third-party compilers or preprocessors. See Other Commands below.
#link
#link <target_filename>
The #link command links together (if necessary) all the files in the link list to the nominated executable or library file.
The file type is determined by the extension of the nominated target file, or, if there is no extension, by the file type
specified in the most recent #system command. If neither are specified, the default is to produce an executable file.
The effect of #link is to set the macro %link_arg to the specified filename.
The Project System maintains a list of those files that are to be used as input to the linker the next time an executable
or library file is created. This list is known as the link list. A filename may be added to the link list using the #pragma link
command.
124
Advanced Topics
For example:
#pragma link (mylib.lib)
However, it is seldom necessary to use #pragma link explicitly, as all the SoftVelocity compilers add the resulting object
file to the link list whenever a source file is compiled using #compile. In addition, when the #link command is
encountered, all required standard library files, and other object files which are imported by those already on the link list
are also added to the list. The link list is cleared after each link.
The #link command differs from the similar #dolink command in that (so far as the Project System can determine), any
additional object files required are automatically added to the link list before linking. This includes any SoftVelocity
library files, and also (with an implicit #autocompile command) all modules imported with IMPORT clauses in
SoftVelocity Modula-2 or with #pragma link statements in SoftVelocity C or SoftVelocity C++ source files. In addition,
#link will determine from the target file type any additional processing that needs to be applied to the output file.
For certain specialized requirements, the use of #link may be inappropriate—for example, if a specialized startup file is
required, or when building library files, where explicit control of exactly which files are included may be preferred. In
such cases, the #dolink command should be used.
#dolink <target_filename>
The #dolink command takes the object files which have previously been added to the link list, and combines them into
an executable or library file (depending on the extension of the nominated target file), if required to keep the target file
up to date. No additional files are added to the link list, so all required files must have been specified previously, by
means of #pragma link, #pragma linkfirst, #compile, and #autocompile. For simple projects, the use of #link is
preferable because the link list is dynamically maintained by the project system, freeing the developer from this
responsibility.
When finished, the #dolink command clears the link list.
See Also:
#pragma link_options (link)
#autocompile
The #autocompile command examines the object files which are currently in the link list, to see which objects they need
to be linked with. This would include objects specified using a #pragma link in a SoftVelocity C or C++ source file, or in
the case of module based languages such as SoftVelocity Modula-2 imported modules.
Each resulting object file, which is not already in the link list, is then compiled (if necessary) and added to the link list. If
there is more than one possible source for a given object file, an error is reported. This process is repeated until the link
list stops changing.
It is not necessary to use #autocompile for simple projects where #link is used rather than #dolink, as #link performs an
implicit #autocompile.
#ignore
#ignore <filename>
#ignore #pragmastring
There are two forms of the #ignore command. The first, where a filename is specified, tells the Project System to ignore
the date of the nominated file when deciding whether or not to compile. This is useful when a ‘safe’ change is made to
a widely used header file, to prevent mass recompile.
The special form #ignore #pragmastring directs the Project System to ignore the #pragma settings when deciding
whether or not to compile a file. This may be useful, for example, when a new compile-time macro has been defined,
but there is no need to recompile everything.
125
Advanced Programming Topics
#implib
#implib <libfilename>
The #implib command is used to create (if necessary) a dynamic link library file. There are two forms of this command,
which operate in slightly different ways. If a single filename is specified, this names an import library file, which is
created (if not up-to-date) from the object files in the link list. The object files are scanned and each public procedure or
variable is exported. For example:
In the second form of the #implib command, an import library filename and a module definition file (.exp—see Module
Definition File below) are both specified, and the library file is created (if not up-to-date) from the symbols named in the
definition file. This form of the command is equivalent to using the tsimplib utility that comes with SoftVelocity C, C++,
and Modula-2.
#implib <expfilename> <libfilename>
Using #implib in the second form requires you to create and maintain the list of exports ‘by hand’, whereas the first form
exports all public names automatically. The use of a module definition file is an advantage if you need to maintain
compatibility with previous versions of an interface, and it also allows you to export only the procedures which need to
be exported.
When #implib is used with a module definition file, the link list is cleared.
126
Advanced Topics
#if
The syntax of the #if command is as follows :
The #elsif part may be omitted, or may be repeated any number of times. The #else part may be omitted.
The expressions are evaluated in order, until one of them yields true, then the following command sequence is
executed. If none of the expressions yield true, and the #else part is present, then the commands following #else are
executed. All other commands are ignored.
The syntax and semantics of boolean expressions are described under Boolean Expressions below.
#error
#error <string>
This command terminates the current project. Under the Clarion environment, the Text Editor is opened at the position
of the #error command, and displays the supplied string as the error message. For example:
#abort
#abort [ on | off ]
This command is used to control whether a failed #compile or #run command will terminate a project. If abort mode is
on, a project will be aborted as soon as a #compile fails, or a #run command produces a non-zero return-code. If abort
mode is off, a project will only be aborted if an internal command fails, including a #link, #implib or #exemod command.
#abort on will set abort mode to on, while #abort off will turn it off. #abort without one of the above arguments will abort
the current project immediately.
The default abort mode is on when running under the Clarion environment.
127
Advanced Programming Topics
User Interface
The following commands allow you to collect information and provide feedback during the make process.
#message
#message <string>
This command displays the specified string in the make display window. This can be used to indicate progress through
the project file, or to display status messages. For example:
#message "finished making %prjname"
#prompt
#prompt <promptstring> [ <defaultstring> ]
This command prompts you to enter a string, by displaying the <promptstring> and waiting for a keyboard entry. The
string you enter is returned as the value of the macro %reply. If <defaultstring> is specified, and no keyboard entry is
made, the <defaultstring> will be used as the value returned to %reply. For example:
#prompt "Command line: " %cline
#set cline = %reply
Boolean Expressions
Boolean expressions used in #if and #elsif commands are made up from the following boolean operators (listed in
order of precedence):
#or
#and
#not
=
#exists
#older
( )
#or
boolean-expression = <factor> { #or <factor> }
A boolean expression containing one or more #or operators yields true if the evaluation of any of the factors yields true.
#and
<factor> = <term> { #and <term> }
A factor containing one or more #and operators yields false if the evaluation of any of the terms yields false.
128
Advanced Topics
#not
<term> = #not <term>
A term proceeded by the #not operator yields true if the evaluation of the term yields false, and vice versa.
= (comparison)
<term> = string = string
A term containing a comparison operator yields true if the strings are identical, otherwise false. == may be used instead
of =.
The = operator and second string may be omitted, in which case the first string is compared against the string "on".
That is,
DemoSwitch =
is equivalent to
DemoSwitch = "on"
The first string may be replaced by an expression of the form name1(name2), where name2 names a #pragma of class
name1. In this case, the expression is replaced by the current setting of the specified #pragma, before the comparison
is made.
#exists
<term> = #exists <file-name>
A term containing the #exists operator yields true if the file exists (after applying redirection to the filename), otherwise
false.
#older
<term> = <file-name> #older <file-name> { , <file-name> }
A term containing the #older operator yields true if the first file specified is older than at least one of the other files
specified, otherwise false. Redirection is applied to all filenames (See The Redirection File below). This operator is
often useful to determine whether a post/pre-processing action needs to be performed. For example:
129
Advanced Programming Topics
#file Commands
The following file system commands are available:
#file adderrors
#file append
#file copy
#file delete
#file move
#file redirect
#file touch
130
Advanced Topics
To capture errors from a program with a different error format, a filter program can be used to translate them. For
example:
If any errors are detected, and abort mode is on, the project will terminate and the errors will be reported in the make
status window.
The macros %errors and %warnings are set to the number of errors and warnings detected.
Other Commands
#run <commandstring>
This command executes the command specified by <commandstring>. A #run command is generated whenever you
add a file to the Programs to execute folder in the Project Tree dialog.
For example:
Filenames within the command string (with the exception of the executable filename itself) are not
automatically subject to redirection - #expand may be used before using #run if this is required.
#include <file-name>
A copy of the contents of the nominated file is inserted in the input stream. <filename> should specify a fully qualified
filename, or an unqualified filename, in which case redirection analysis is applied (see The Redirection File above).
The current values of the link list, #pragma settings, and macros are fully available to the #include statements. In other
words, the #include statements are handled as though they resided within the including .prj file.
131
Advanced Programming Topics
#call <file-name>
A copy of the contents of the nominated file is inserted in the input stream. <filename> should specify a fully qualified
filename, or an unqualified filename, in which case redirection analysis is applied (see The Redirection File above).
The current values of the link list, #pragma settings, and macros are not available to the #call statements, and the #call
statements cannot modify these values in the calling environment. In other words, #call statements are handled as a
process that is completely separate from the calling process.
#declare_compiler <file_extension> = <executed_macro>
This defines a macro which is invoked when compiling source files with an extension matching the first parameter. The
macros %src and %obj, are set to the names of the source and object files.
Generally, you will not have to use this command explicitly, as all SoftVelocity compilers are pre-declared in the Project
System. For example the following is to invoke MASM
#declare_compiler asm=
‘#set make=%%remake
#if %%make #then
#edit save %%src
#expand %%src
#set _masmsrc=%%opath
#expand %%obj
#set _masmobj=%%cpath
#run "masm %%_masmsrc,%%_masmobj/MX/e; >masmtmp.$$$"
#file adderrors masmtmp.$$$
#file delete masmtmp.$$$
#endif
#pragma link(%%obj)’
132
Advanced Topics
SoftVelocity #pragmas
Modula-2 Pragma Syntax
Old-type Compiler Directives
C_and_C++_Pragma_Syntax
Project System Pragma Syntax
Pragma Classes
Call #pragmas
Data #pragmas
Code #pragmas
Check #pragmas
Name #pragmas
Optimize #pragmas
Debug #pragmas
Module #pragmas
Option #pragmas
Warn #pragmas
Project #pragmas
Save/Restore #pragmas
Link #pragmas
Link_Option #pragmas
Define #pragmas
Pre-defined Flags
All SoftVelocity languages, and the Project System, use a common set of compiler options known as #pragmas. In
general, pragmas may appear in the source code or in a project file, and the effect will be the same.
A pragma can be used in the Project language, C++ code, Modula-2 code, or Clarion code. Some only work in
certain places. A ‘P’ to the right of the pragma indicates it can be used in the Project language, a ‘C’ indicates
it can be used in C++ code, a ‘M’ indicates it can be used in Modula-2 code, and a ‘W’ indicates that it can be
used in Clarion code (CLW files).
133
Advanced Programming Topics
However, you may find that you have to make other changes as well as the effect of the pragma is different
from the $J directive:
$K (C calling convention). This is not supported. Instead, you should use the pragma:
(*# call( c_conv => on ) *)
$M (code segment name). This is supported but adds the suffix _TEXT to the name instead of the C_ prefix.
$P (external names for local procedures). This is no longer supported. It is no longer applicable.
$Q (procedure tracing). This is no longer supported. Instead, you should use the pragma:
(*# debug( proc_trace => on ) *)
This enables a different method of tracing procedures. Refer to the proc_trace pragma for further details.
$X (80x87 stack spilling). This is no longer supported (and is no longer necessary).
$Z (NIL pointer checks). This still does NIL pointer checks but no longer clears memory.
$@ (preserve DS). This is no longer supported.
The support for these directives has been included with later systems so that your old programs and modules will
recompile with minimum changes. However, you should avoid using the old directives with new programs, and use
pragmas instead.
Pragmas in the Project System may also be specified in the #compile command, to apply to a single compilation. For
example:
#compile mandel.mod /debug(vid=>on)
134
Advanced Topics
Pragma Classes
A #pragma takes the form #pragma class(name=>value). The #pragma classes are as follows:
Call #pragmas
Check #pragmas
Code #pragmas
Data #pragmas
Debug #pragmas
Define #pragmas
Link and Linkfirst #pragmas
Link_option #pragmas
Module #pragmas
Name #pragmas
Optimize #pragmas
Option #pragmas
Project #pragmas
Save and Restore #pragmas
Warn #pragmas
135
Advanced Programming Topics
Call #pragmas
#pragmas with the class name call affect all aspects of calling conventions, code segments, and code pointers. The
current settings of the call #pragmas at the point at which a procedure’s definition is encountered, determines the
calling convention that is used to call the procedure. SoftVelocity compilers detect if an inconsistent calling convention
is used when a procedure is called. The type-safe linker reports an error if the calling conventions attributed to a given
procedure do not match in every object file.
The following call #pragmas are available:
#pragma call(c_conv => on | off)
#pragma call(ds_entry => identifier)
#pragma call(ds_eq_ss => on | off)
#pragma call(inline => on | off)
#pragma call(inline_max => Number)
#pragma call(near_call => on | off)
#pragma call(o_a_copy => on | off)
#pragma call(o_a_size => on | off)
#pragma call(opt_var_arg => on | off)
#pragma call(reg_param => RegList)
#pragma call(reg_return => RegList)
#pragma call(reg_saved => RegList)
#pragma call(result_optional => on | off)
#pragma call(same_ds => on | off)
#pragma call(seg_name => identifier)
#pragma call(set_jmp => on | off)
#pragma call(standard_conv => on | off)
#pragma call(var_arg => on | off)
A pragma can be used in the Project language, C++ code, Modula-2 code, or Clarion code. Some only work in
certain places. A ‘P’ to the right of the pragma indicates it can be used in the Project language, a ‘C’ indicates
it can be used in C++ code, a ‘M’ indicates it can be used in Modula-2 code, and a ‘W’ indicates that it can be
used in Clarion code (CLW files).
136
Advanced Topics
You can also use the cdecl keyword in C and C++ to achieve the same effect.
See also the standard_conv #pragma, which has the same effect for C and C++, but is ignored for Modula-2. The
standard_conv #pragma is set off by default.
#pragma save
#pragma call(inline => on, reg_param => (dx,ax))
static void outp(int port, unsigned char byt)=
{
0xEE, /* out dx,al */
};
#pragma restore
makes outp an inline procedure, so a call to it appears as a single 80x86 machine instruction: out dx,al.
The default setting is language dependant, and is not defined by the Project System.
137
Advanced Programming Topics
This example indicates that on entry to the procedure, DS will point to the segment named MYDATA_DGROUP.
Note that the es and ds registers will only be used for the high word of a 4-byte parameter where that parameter is of
pointer type. If either the low or high word cannot be allocated, then the whole parameter is passed on the stack.
When the compiler exhausts the list of registers, it passes the parameter on the stack. If you specify an empty list, the
compiler uses the stack for all parameters.
The default setting for the SoftVelocity calling convention is:
#pragma call(reg_param=>(ax,bx,cx,dx,st0,st6,st5,st4,st3))
138
Advanced Topics
This #pragma has no effect for value parameter open arrays, unless the o_a_copy #pragma is set off.
The default setting is on.
(*# save *)
(*# module( result_optional => on ) *)
PROCEDURE FuncProc(x: CHAR): CARDINAL;
(*# restore *)
139
Advanced Programming Topics
i := FuncProc(‘a’);
FuncProc(‘a’);
This is only useful when the called procedure has a side effect that is more important than the result. It is particularly
useful when calling SoftVelocity C library procedures.
The default setting is off.
Procedures are not inlined if the body has not been compiled before the call.
140
Advanced Topics
Check #pragmas
#pragmas with the class name check control run-time error checking. These can help you to locate erroneous program
logic, but at the expense of slower execution. All these #pragmas default to off.
When a run-time check detects an error, the default action is to terminate the process and create the file CWLOG.TXT.
The following check #pragmas are available:
#pragma check(guard => on | off)
#pragma check(index => on | off)
#pragma check(nil_ptr => on | off)
#pragma check(overflow => on | off)
#pragma check(range => on | off)
#pragma check(stack => on | off)
A pragma can be used in the Project language, C++ code, Modula-2 code, or Clarion code. Some only work in
certain places. A ‘P’ to the right of the pragma indicates it can be used in the Project language, a ‘C’ indicates
it can be used in C++ code, a ‘M’ indicates it can be used in Modula-2 code, and a ‘W’ indicates that it can be
used in Clarion code (CLW files).
141
Advanced Programming Topics
Code #pragmas
#pragmas with the class name code control internal code generation optimizations.
The following code #pragmas are available:
#pragma code(opt_var_arg => on | off)
#pragma code(standard_float => on | off)
A pragma can be used in the Project language, C++ code, Modula-2 code, or Clarion code. Some only work in
certain places. A ‘P’ to the right of the pragma indicates it can be used in the Project language, a ‘C’ indicates
it can be used in C++ code, a ‘M’ indicates it can be used in Modula-2 code, and a ‘W’ indicates that it can be
used in Clarion code (CLW files).
142
Advanced Topics
Data #pragmas
#pragmas with the class name data affect data segmentation, data pointers and all aspects of data layout. The current
settings of the data #pragmas at the point of a variable’s declaration will affect the way in which it is accessed.
The following data #pragmas are available:
A pragma can be used in the Project language, C++ code, Modula-2 code, or Clarion code. Some only work in
certain places. A ‘P’ to the right of the pragma indicates it can be used in the Project language, a ‘C’ indicates
it can be used in C++ code, a ‘M’ indicates it can be used in Modula-2 code, and a ‘W’ indicates that it can be
used in Clarion code (CLW files).
You can also specify null, to indicate the names _BSS and _DATA. The default value is null in all models except for
extra large and multi-thread. For example:
#pragma data(seg_name => null)
143
Advanced Programming Topics
makes the name of the default segments MYDATA_DATA and MYDATA_BSS in group MYDATA. The compiler
assumes external data objects to be in the same segment.
makes the name of the default segments MYDATA_DATA and MYDATA_BSS in group MYDATA. This #pragma is not
particularly useful in Modula-2 except for interfacing to C.
will allow you to use the same name in different alternatives, if the fields are located at the same offset in the variant
record and they have the same data type.
The default setting is off.
144
Advanced Topics
will force the compiler to store them as two-byte quantities. This is particularly useful for interfacing to third-party
libraries and operating system calls that expect a word value. Without this #pragma the enumeration would be byte
rather than word size.
The default setting is on.
145
Advanced Programming Topics
146
Advanced Topics
Debug #pragmas
#pragmas with the class name debug control the amount of additional information produced by the code-generator to
assist debugging programs.
The following debug #pragmas are available.
A pragma can be used in the Project language, C++ code, Modula-2 code, or Clarion code. Some only work in
certain places. A ‘P’ to the right of the pragma indicates it can be used in the Project language, a ‘C’ indicates
it can be used in C++ code, a ‘M’ indicates it can be used in Modula-2 code, and a ‘W’ indicates that it can be
used in Clarion code (CLW files).
This #pragma disables the register usage and stack frame optimizations, allowing full access to variables
within the debugger. All local variables are treated as volatile, to ensure that their values are not held in registers
across statements, thus ensuring that the debugger can access their values at all times.
When min, the compiler performs the optimizations described above, and does not treat local variables as volatile. The
debugger can still be used, but cannot reference local variables and some stack frames.
When off, the compiler generates no debugger information, thus speeding compile, generating the best possible code,
and saving disk space. The default setting is off.
You should ensure that this #pragma is off for the EnterProc and ExitProc procedures themselves, otherwise
infinite recursion will occur and your program will undoubtedly crash.
The two procedures must be visible to the module in which proc_trace is set on. This means that the module itself must
define the procedures ExitProc and EnterProc or the module must specifically import them using an unqualified import.
The default setting is off.
147
Advanced Programming Topics
148
Advanced Topics
Define #pragmas
A #pragma whose class name is define is used to define a conditional compile symbol for subsequent compiles. The
symbol is available for interrogation by the OMIT and COMPILE compiler directives. See the Language Reference for
more information. This #pragma may only be used in project files.
A define #pragma takes the form:
#pragma define(ident=>value)
where ident names the symbol and value specifies the value it is given.
For Modula-2, the given identifier is defined as a boolean constant with value TRUE if the value on was specified,
otherwise FALSE. For C and C++, the given identifier is defined as a macro. If the value on is specified, the macro is
defined to the value 1. If the value off is specified, the macro is not defined. Any other value will cause the identifier to
be defined as a macro expanding to the given value. Only a single C or C++ token may be specified, or the compiler
will report an error. To define a macro where the value is a string literal, use a command of the form #pragma define
(name => ‘"fred"’).
A pragma can be used in the Project language, C++ code, Modula-2 code, or Clarion code. Some only work in
certain places. A ‘P’ to the right of the pragma indicates it can be used in the Project language, a ‘C’ indicates
it can be used in C++ code, a ‘M’ indicates it can be used in Modula-2 code, and a ‘W’ indicates that it can be
used in Clarion code (CLW files).
149
Advanced Programming Topics
show the assert dialog. This code can be replaced by setting SYSTEM{PROP:AssertHook} property. If the pragma is
set to 1, the call to show the assert dialog is generated regardless of the value of the debug(vid) pragma.
#pragma define(compatible_modulus=> p
on | off )
Controls the sign of the result of modulus division. If the pragma is set to zero (OFF), the modulus result uses the sign
of the divisor. If the pragma is set to one (ON), the modulus result uses the sign of the dividend.The default is off.
Example:
If compatible_modulus = 0, then
42 % -5 = -2 (-5 is the divisor(-) )
If compatible_modulus = 1, then
42 % -5 = 2 (42 is dividend (+))
150
Advanced Topics
The EnterProc is called at the beginning of each procedure and LeaveProc at the end.
151
Advanced Programming Topics
152
Advanced Topics
Link #pragmas
These #pragmas may be specified in a project file, in which case the nominated files are added immediately to the link
list. In addition, the link #pragma may be specified in a C or C++ source file, in which case the nominated files will be
added to the link list when an autocompile command is executed in the Project System, if any files already on the link
list had this #pragma specified. For example:
If no extension is given .obj is assumed. Files specified using #pragma link are added to the end of the link list (unless
already present). A file specified using #pragma linkfirst is linked before the link list. Only one file may be specified for
each link using #pragma linkfirst.
153
Advanced Programming Topics
Link_Option #pragmas
#pragmas with the class name link_option are used to specify linker options. These #pragmas may only occur in
project files.
The following link_option #pragmas are available:
A pragma can be used in the Project language, C++ code, Modula-2 code, or Clarion code. Some only work in
certain places. A ‘P’ to the right of the pragma indicates it can be used in the Project language, a ‘C’ indicates
it can be used in C++ code, a ‘M’ indicates it can be used in Modula-2 code, and a ‘W’ indicates that it can be
used in Clarion code (CLW files).
154
Advanced Topics
155
Advanced Programming Topics
Module #pragmas
#pragmas with the class name module control options that apply to an entire source file or module. These #pragmas
should be specified at the top of any source files to which they apply, or in the project file.
The following module #pragmas are available:
A pragma can be used in the Project language, C++ code, Modula-2 code, or Clarion code. Some only work in
certain places. A ‘P’ to the right of the pragma indicates it can be used in the Project language, a ‘C’ indicates
it can be used in C++ code, a ‘M’ indicates it can be used in Modula-2 code, and a ‘W’ indicates that it can be
used in Clarion code (CLW files).
If an implementation module sets this #pragma off, then there is a knock-on effect, i.e., all imported modules
must also have init_code set to off.
The default setting is on.
156
Advanced Topics
157
Advanced Programming Topics
Name #pragmas
#pragmas with the class name control aspects of linkage naming. However, the C programmer should also be familiar
with C name mangling and extern declarations.
The following name #pragmas are available:
#pragma name(prefix => (none | modula | c | os2_lib | windows))
#pragma name(prefix => string)
#pragma name(upper_case => on | off)
A pragma can be used in the Project language, C++ code, Modula-2 code, or Clarion code. Some only work in
certain places. A ‘P’ to the right of the pragma indicates it can be used in the Project language, a ‘C’ indicates
it can be used in C++ code, a ‘M’ indicates it can be used in Modula-2 code, and a ‘W’ indicates that it can be
used in Clarion code (CLW files).
Modula Use the SoftVelocity Modula-2 naming convention of prefixing all external names
with the name of the module and an ‘@’ or a ‘$’.
c Use the C naming convention (adds an underbar, ‘_’ to all external names).
os2_lib Use the OS/2 library standard (prefix all external names with the module name).
158
Advanced Topics
In C, a Pascal or Modula2 linkage specification can specify a module name within the linkage specification, in which
case the use of this #pragma is not necessary.
The default setting is language-dependent. The Project System does not set a default value for this macro.
159
Advanced Programming Topics
Optimize #pragmas
#pragmas with the class name optimize control optimizations performed by the SoftVelocity code generator. By default,
all optimizations are enabled. Turning off an optimization will result in poorer code quality, and is unlikely to have a
significant impact on compile times.
The following optimize #pragmas are available:
A pragma can be used in the Project language, C++ code, Modula-2 code, or Clarion code. Some only work in
certain places. A ‘P’ to the right of the pragma indicates it can be used in the Project language, a ‘C’ indicates
it can be used in C++ code, a ‘M’ indicates it can be used in Modula-2 code, and a ‘W’ indicates that it can be
used in Clarion code (CLW files).
160
Advanced Topics
161
Advanced Programming Topics
Option #pragmas
#pragmas with the class name option control language-dependent options, such as SoftVelocity extensions. The
following option #pragmas are available:
A pragma can be used in the Project language, C++ code, Modula-2 code, or Clarion code. Some only work in
certain places. A ‘P’ to the right of the pragma indicates it can be used in the Project language, a ‘C’ indicates
it can be used in C++ code, a ‘M’ indicates it can be used in Modula-2 code, and a ‘W’ indicates that it can be
used in Clarion code (CLW files).
162
Advanced Topics
When off, nested comments cause an error message. The default is off, allowing the compiler to trap unterminated
comments more easily and make it conform with ANSI C.
163
Advanced Programming Topics
Project #pragmas
A #pragma with the class name project is used to pass information from a compile to the Project System. The value of
the #pragma should be a string, which is then stored in the object file for use by the Project System. Whenever an
object file is added to the link list, the text specified using this #pragma is executed as a Project System command. For
example, if a header file includes the line:
#pragma project("#set myflag=on")
then whenever a source file that includes this header file is included in a project, the Project System macro myflag will
be set. This might be used for processing later in the project file.
This #pragma may only appear in source files, not in a project file.
Save/Restore #pragmas
The save #pragma saves the entire #pragma state, so you can later restore it with a restore #pragma. The save and
restore #pragmas work in a stack-like manner, thus allowing you to nest them. For example:
There is no limit on the number of saves, except the amount of memory available. These #pragmas may be used in
source files or in a project file.
164
Advanced Topics
Warn #pragmas
#pragmas with the class name warn control the generation of compiler warnings. These #pragmas are only available
under the project language and C/C++.
The warnings given by SoftVelocity C and C++ help you to check, as far as possible, common coding mistakes. Since
no compiler can determine your intentions, you may get warnings even if your code is correct. Your code may generate
some warnings more than others, so SoftVelocity allows you to customize which warning checks are performed.
You can set each of the warning options to on, off, or err. When on, SoftVelocity checks the code for that warning and
reports the problem, but the problem does not stop the compile or linking. When off, SoftVelocity ignores the warning.
When err, SoftVelocity checks the code for the warning, reports the problem, and does not allow linking until you have
fixed the problem.
SoftVelocity C and C++ check the code and produce a warning for a good reason. Indeed, to use your non-
ANSI C code, SoftVelocity C uses a minimal set of warning messages by default. You should, therefore, think twice
before turning off any of the default warning messages. We advise that you keep all the warnings either on or err.
The following warn #pragmas are available:
#pragma warn(wacc => on | off | err)
#pragma warn(wait => on | off | err)
#pragma warn(wall => on | off | err)
#pragma warn(watr => on | off | err)
#pragma warn(wcic => on | off | err)
#pragma warn(wcld => on | off | err)
#pragma warn(wclt => on | off | err)
#pragma warn(wcne => on | off | err)
#pragma warn(wcor => on | off | err)
#pragma warn(wcrt => on | off | err)
#pragma warn(wdel => on | off | err)
#pragma warn(wdne => on | off | err)
#pragma warn(wdnu => on | off | err)
#pragma warn(wetb => on | off | err)
#pragma warn(wfnd => on | off | err)
#pragma warn(wftn => on | off | err)
#pragma warn(wnid => on | off | err)
#pragma warn(wnre => on | off | err)
#pragma warn(wnrv => on | off | err)
#pragma warn(wntf => on | off | err)
#pragma warn(wovl => on | off | err)
#pragma warn(wovr => on | off | err)
#pragma warn(wpcv => on | off | err)
#pragma warn(wpic => on | off | err)
#pragma warn(wpin => on | off | err)
#pragma warn(wpnd => on | off | err)
#pragma warn(wpnu => on | off | err)
165
Advanced Programming Topics
A pragma can be used in the Project language, C++ code, Modula-2 code, or Clarion code. Some only work in
certain places. A ‘P’ to the right of the pragma indicates it can be used in the Project language, a ‘C’ indicates
it can be used in C++ code, a ‘M’ indicates it can be used in Modula-2 code, and a ‘W’ indicates that it can be
used in Clarion code (CLW files).
The static storage class always takes preference. The default setting is on.
166
Advanced Topics
167
Advanced Programming Topics
168
Advanced Topics
int func(IntegerByPromotion);
char IntegerByPromotion;
/* INCOMPATIBLE */
{
...
}
This is a violation of the ANSI C standard regarding compatible function declarations.The default setting is
on.
169
Advanced Programming Topics
Default access specifier used for base class. This warning is issued if a base class specification does not have an
access specifier and the default access is used (i.e. public for a struct and private for a class). The default setting is on.
170
Advanced Topics
_CWVER_ Four digit number. The top two digits are the major version of Clarion. The
lower two digits are the minor version. For the initial release of Clarion 6.0
this is set to 6000, 6.1, 6100, etc.
_ABCDllMode_ Used by the ABC template chain on all CLASS definitions to indicate that
the CLASS is declared in an external .DLL. A project DEFINE is used to
toggle the DLL mode.
171
Advanced Programming Topics
ALLDRV.PR
#system win dll
#model clarion
#pragma define(DEMO_VERSION=>%demo)
#set domodels=
‘
#abort on
#set dowin32=off #set dolib=off
#call %%prjfile
#set dowin32=off #set dolib=on
#call %%prjfile
#set dowin32=off #set dolib=off
#abort off
#set dowin32=on #set dolib=off
172
Advanced Topics
#call %%prjfile
#set dowin32=on #set dolib=on
#call %%prjfile
#abort on
‘
#set domodels=
‘
#set dowin32=off #set dolib=off
#call %%prjfile
#set dowin32=off #set dolib=on
#call %%prjfile
#set dowin32=off #set dolib=off
‘
ORACLE.PR:
#noedit
-------------------------------------------------------------------
-------------------------------------------------------------------
-- ORACLE.PR Oracle Driver project file
-------------------------------------------------------------------
-------------------------------------------------------------------
#system win dll --target OS is windows, dll executable
#model clarion --memory model is clarion
-- Set default macro values. These "switches" will control the make process
#set drv = ORA
173
Advanced Programming Topics
174
Advanced Topics
SQLFILES.PR:
-- Release version: Disable all debugging and tracing
#if "%release"="on" #then
#set drvdebug = off
#set kitdebug = off
#set sqldebug = off
#set trace = off
#set heapchk = off
#endif
175
Advanced Programming Topics
176
Advanced Topics
DRVKIT.PI:
#noedit
-----------------------------------------------------------------------
-----------------------------------------------------------------------
-- DRVKIT.PI Driver Kit project include file
-----------------------------------------------------------------------
-----------------------------------------------------------------------
177
Advanced Programming Topics
#else
#system win dll -- win16
#endif
#model clarion
178
Advanced Topics
-- Common Code: Some Driver Kit code is merged when linking multiple
-- File Driver Libraries
#if "%common"="on" #then --Conditionally...
#pragma define(COMMON_CODE=>on) -- define compiler switch
#endif
179
Advanced Programming Topics
180
Advanced Topics
#endif
181
Advanced Programming Topics
#link %%drvname%%
182
Advanced Topics
Statement Attribute
IMAGE_VERSION n[.m] Values of n and m set the image major and minor version
fields in PE optional header respectively. n and m must be
decimal format. Default values for these fields are zero (0). n
and m are WORD types and have a maximum value of 65535.
183
Advanced Programming Topics
LIBRARY MyDLL
; Sample export file
EXPORTS
Func1 @1
Var1 @2
Func2 @3
Func3 @4
Func4 @5
appname If appname is given, it becomes the name of the application as it is known by the operating system. If
no appname is given, the name of the executable file, with the extension removed, becomes the
name of the application.
apptype Used to control the program’s behavior under Windows. This information is kept in the executable-file
header. The apptype field may have one of the following values:
WINDOWAPI
The application uses the API provided by Windows and must be executed in the Windows
environment.
GUI
Same as WINDOWAPI.
CUI
The program uses a character based user interface, like DOS.
If the NAME statement is included in the module-definition file, then the LIBRARY statement cannot appear.
If neither a NAME statement nor a LIBRARY statement appears in a module-definition file, NAME is assumed.
The following example assigns the name wdemo to the application being defined:
NAME wdemo WINDOWAPI
184
Advanced Topics
libraryname If libraryname is specified, it becomes the name of the library as it is known by the operating system.
This name can be any valid file name. If no libraryname is given, the name of the executable file,
with the extension removed, becomes the name of the library.
initialization The initialization field is optional and can have one of the two values listed below. If neither is given,
then the initialization default is INITINSTANCE.
INITGLOBAL
The library-initialization routine is called only when the library module is initially loaded into
memory.
INITINSTANCE
The library-initialization routine is called each time a new process gains access to the library.
If the LIBRARY statement is included in a module definition file, then the NAME statement cannot appear.
The following example assigns the name mydll to the dynamic-link module being defined, and specifies that library
initialization is performed each time a new process gains access to myDLL:
LIBRARY myDLL INITINSTANCE
185
Advanced Programming Topics
Specifies the base memory location of the module. This statement is not generated by default from the Clarion
environment, but can be implemented using the Rebase template. If no IMAGE_BASE is specified in the EXP then the
module is assigned the default address (normally 00400000h) and conflicts are handled automatically by the Windows
loader. (Using the Clarion IDE you can add an image_base line to the EXP file in the global embed named "Before the
Export List".) The syntax for the IMAGE_BASE statement is as follows:
IMAGE_BASE address
where address is a 32-bit address specified in decimal or hex. If hex, then the address is followed by an "h". The address
must be divisible by 64KB (65,536 or 00010000h). It must be in the range of 00400000h to 70000000h for Windows 9x.
Under Windows NT the address lower limit is 00010000h.
Example:
IMAGE_BASE 00600000h
It's best to supply the address in hex since all documentation on the OS will show a hex address and it's easy to tell
you've got a good address because it always ends with 4 zeros.
186
Advanced Topics
FILE_ALIGNMENT number
EXPORTS
exportdefinitions
The EXPORTS keyword marks the beginning of the export definitions. It may be followed by up to 3072 export definitions,
each on a separate line. You should give an export definition for each dynamic-link routine that you want to make
available to other modules. The syntax for an export definition is as follows:
187
Advanced Programming Topics
Exporting CLASSes
Exporting CLASS declarations requires a special form of export definition.
You must create two export definitions for the CLASS itself. The first begins with VMT$ followed by the name of the
CLASS as the entryname. The second begins with TYPE$ followed by the name of the CLASS as the entryname. These
are followed by an export definition for each method in the CLASS to export whose pwords must begin with the name of
the CLASS as the first parameter.
For example:
EXPORTS
VMT$MYCLASS @?
TYPE$MYCLASS @?
FIRSTMETHOD@F7MYCLASS @?
SECONDMETHOD@F7MYCLASS @?
188
Advanced Topics
Here is an example of the export file (EntryPoint is the procedure entry into the DLL)
--------------------------------------
EXPORTS
EntryPoint@F @?
__checkversion @?
__sysstart @?
__sysinit @?
_exit @?
Cla$code @?
Cla$init @?
Wsl$Closedown @?
In this example, the entry point procedure name in the Local DLL is: "EntryPoint"
Use the Inside the Export List Global Embed to add to your export list within the application.
The starter EXE must use External link mode. The source is written so that it just calls the DLL's entry point procedure.
Example starter EXE code:
----------------------------
PROGRAM MAP MODULE('') EntryPoint() END END CODE EntryPoint
189
Advanced Programming Topics
A version script file is simply a text file with the extension of .Version. When included into a Clarion project (application or
hand coded), the version file stamps, or writes, a variety of information into the target executable. This information can be
viewed by right-clicking on the executable file, and selecting Properties from the popup menu. A Version tab should be
available with the designated version information.
More detail regarding the standard format of the version info script can be found at the following URL:
Microsoft Version Control Information
VS_VERSION_INFO VERSIONINFO
...
END
If the LANGUAGE directive is present in the version file, the language code for the resource target executable is
set. This allows a developer to have multiple version info resources for different languages.
2. In the version information group, numbers must use one of the following formats:
- decimal numbers (0-9)
- hexadecimal numbers in C/C++ format (Example: 0x3fL)
- hexadecimal numbers in Modula-2/Clarion format (Example: 040904E4)
- binary numbers in Modula-2/Clarion format
3. Strings must be of C/C++ format. The \u and \x escape characters are not supported in strings.
4. #include directives are not supported, but all standard mnemonics for the version info related constants are built in to
the compiler.
190
Advanced Topics
1 VERSIONINFO
FILEVERSION 1,0,0,1
PRODUCTVERSION 1,0,0,1
FILEFLAGSMASK 0x3fL
FILEFLAGS 0
FILEOS VOS__WINDOWS32
FILETYPE VFT_APP
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904E4"
BEGIN
VALUE "CompanyName", "\0"
VALUE "FileDescription", "This just a test\0"
VALUE "FileVersion", "1, 0, 0, 1\0"
VALUE "InternalName", "Version Info Script Example\0"
VALUE "LegalCopyright", "Copyright (C) 2003\0"
VALUE "LegalTrademarks", "\0"
VALUE "OriginalFilename", "TEST\0"
VALUE "ProductName", "Version Info Script compiler\0"
VALUE "ProductVersion", "1, 0, 0, 1\0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1252
VALUE "Translation", 0x419, 1251
VALUE "ÒÅÑÒ", 0x409, 1111
END
END
This file is a working example. You can use this as a template for your real world version script files. Simply copy this
example to a text file, name it yourfilename.version, and include it in the Library, object, and resource files section of
the Project Tree.
191
Advanced Programming Topics
Overview
SoftVelocity has 32-bit C++ and Modula-2 compilers that can integrate into the Clarion environment. The 32-bit compilers
generate object code for the Windows 95/NT/2000/XP environments. You must be running your development environment
under a 32-bit environment to generate applications for a 32-bit environment.
Clarion also has object code generation capability that rivals that of many C compilers. You can then enhance that
application with any low-level functions you need. These 3rd Generation Language (3GL) compilers enable the developer
to include 3GL code modules directly into a Clarion project, giving unparalleled functionality and versatility. This mix of a
Rapid Application Development (RAD) 4GL (4th Generation Language)—Clarion—and traditional 3GL compilers, makes
Clarion an exceptional application development tool.
So why use C, C++, Pascal, or Modula-2 code at all in a Clarion application? Because there are libraries available
(statistical, financial, graphics, communications, and many more) which could significantly cut the development time of an
application that requires these capabilities. Many of these libraries are written in C, and many powerful C++, Pascal, and
Modula-2, libraries are also available. Clarion allows you to use these libraries in their "native" form without "re-inventing
the wheel."
Throughout this topic, we assume you have a good knowledge of the Clarion development environment, the
Clarion language, and the 3GL in question. The code examples assume that you use a SoftVelocity compiler (other
compiler’s requirements are also discussed). Since Clarion uses SoftVelocity code generation and linking technology, it is
easiest to link code produced with SoftVelocity Compilers to Clarion applications. Clarion can link code produced by other
third party compilers; however, some care is required as well as a good understanding of the operation of both compilers.
It is not generally possible to directly link C++ code produced by C++ compilers other than SoftVelocity.
192
Advanced Topics
Compiler Integration
With the SoftVelocity 3GL compilers installed, the Clarion development environment takes all the action necessary to
call the correct compiler for each source module in the application. You cannot mix languages in a single source
module; however, an application can contain any number of source modules written in any of the 3GLs or Clarion.
The development environment calls the correct compiler for each module at compile time by looking at the source file
extensions, as follows:
Source File Extension Compiler Called
.CLW Clarion
.CPP or .C C++
.MOD Modula-2
Source files with any other extensions will generate an ‘Unknown compiler for ...’ error message at compile time.
The Development Environment will also ensure that all modules are linked correctly and that the SoftVelocity
SmartLinker is given all the information that it requires.
MODULE(‘module name’)
INCLUDE(‘YourInc.Inc’)
END
The Application Generator generates the MODULE and END statements. Failure to correctly prototype your
functions will almost certainly result in a General Protection Fault at run time.
2. Use the Text Editor to write your 3GL code.
Be sure to save the code with a file extension that the compiler can recognize (.C, .CPP, .MOD, or .PAS).
3. Add the module to the application as an External Source Module.
Select Application > Insert Module from the main menu. Then select class External Source from the Select
Module Type dialog. Press the ellipsis button, and in the Open File dialog, enter the name of the 3GL module in
the Name field and click on the Open button, Back in the Module Properties dialog, enter the name of the
include file in the Map Include File field of the Module Properties dialog. Press OK to add to the application tree.
4. Compile and run the application.
193
Advanced Programming Topics
MODULE(‘module name’)
MyFunc(*CSTRING),CSTRING,RAW,PASCAL,NAME(‘_MyFunc’)
END
You must include a complete MODULE structure in your Clarion MAP for all your 3GL modules. Failure to
correctly prototype your functions will almost certainly result in a General Protection Fault at run time.
2. Use the Text Editor to write your 3GL code, saving the code with a file extension that the compiler can recognize
(.C, .CPP, .MOD, or .PAS).
3. Add the module to the Project as an External Source File.
Select Project > Edit from the main menu. Highlight External Source Files then press the Add File... button.
Select the 3GL source mofule from the standard file open dialog that appears.
4. Compile and run the application.
194
Advanced Topics
SIGNED int
195
Advanced Programming Topics
Clarion STRING variables are normally passed as two parameters: first, a UNSIGNED which contains the length of the
data buffer; second, the address of the data. CSTRINGs and PSTRINGs are passed the same as STRINGs (as two
parameters). The RAW attribute can be used in the Clarion prototype to pass only the address of the string data to
external 3GL functions (Clarion language procedures do not need, or support, RAW).
C and C++ Data Type Equivalents
The following data type equivalents can be used with C or C++ code. These typedefs should appear in the .H header
file referenced by the C or C++ code. The CLA prefix is used to avoid name clashes with third party libraries.
Clarion DATE and TIME data types may be passed to C functions as a CLALONG, the CLADATE and CLATIME
unions can then be used to resolve the elements of the date or time from the CLALONG value.
typedef union {
CLALONG n;
struct {
CLABYTE ucDay;
CLABYTE ucMonth;
CLAUSHORT usYear;
} s;
} CLADATE;
typedef union {
CLALONG n;
struct {
CLABYTE ucHund;
CLABYTE ucSecond;
CLABYTE ucMinute;
CLABYTE ucHour;
} s;
} CLATIME;
Because of Clarion’s two-parameter method of passing STRINGs, the CLASTRING structure is useful for certain
internal uses, but cannot be used to accept parameters from Clarion:
typedef struct {
char *pucString;
196
Advanced Topics
CLALONG usLen
} CLASTRING;
Clarion STRING variables are not NULL terminated, they are padded with spaces up to the length of the data buffer.
The trailing spaces can be removed by using the Clarion CLIP procedure. The following code declares a STRING of 20
characters, assigns some data into it, and passes it as a parameter to a C or C++ function.
StringVar STRING(20)
CODE
StringVar = 'Hello World...'
C_Write_Function(StringVar)
In the above example, usLen would have a value of 20 and bData would be padded with trailing spaces. This padding
would be written to the screen by C_Write_Function(). Many C routines expect a string to be NULL terminated. To
address this issue, Clarion provides the CSTRING data type. CSTRING variables are automatically NULL terminated
when data is assigned to them. This makes it possible for existing C routines to operate on the data.
A Clarion GROUP may be declared to contain related data. A group is roughly equivalent to a C or C++ struct. When
passed as a parameter to a procedure, GROUPs are normally passed as three parameters: first, an UNSIGNED is
passed which contains the size of the GROUP; second, the address of the GROUP structure; and third, the address of
a buffer containing a type descriptor for the GROUP. The contents of the type descriptor are not discussed here and
are subject to change in future versions of Clarion. GROUPs may be nested, and other GROUPs may be defined to
assume the same structure as a previously declared GROUP. There are several forms of declaration for Clarion
GROUPs:
This form of definition reserves space for Struct1 and is equivalent to the C definition:
struct {
CLAULONG ul1;
CLAULONG ul2;
197
Advanced Programming Topics
} Struct1;
In the following example, the declaration of Struct2 declares a GROUP similar to that defined by Struct1, however no
space is reserved. In practice there need not be any instances of Struct2 defined.
typedef struct {
CLAULONG ul3;
CLAULONG ul4;
} Struct2
In the following example, the definitions of Struct3 and Struct4 define them to be LIKE(Struct2), i.e. of the same internal
structure. In order to distinguish members of Struct3 and Struct4 from those of Struct2 the S3 and S4 prefixes must be
used. Struct3 and Struct4 define instances of Struct2 (which is not necessarily defined anywhere). In both cases space
is reserved.
Struct3 LIKE(Struct2)
Struct4 LIKE(Struct2)
Struct3 S3;
Struct4 S4;
198
Advanced Topics
typedef struct {
struct {
CLAULONG ul5;
CLAULONG ul6;
} Struct6;
} Struct5;
CONST
BYTE ::= BYTE;
SHORT ::= INTEGER (16-bit);
USHORT ::= CARDINAL (16-bit);
LONG ::= LONGINT;
ULONG ::= LONGCARD;
SREAL ::= REAL;
REAL ::= LONGREAL;
Clarion DATE and TIME data types may be passed to Modula-2 procedures as a LONG, the DATE and TIME
RECORDs can then be used to resolve the elements of the date or time from the LONG value.
DATE = RECORD
CASE : BOOLEAN OF
| TRUE:
l : LONG;
ELSE
ucDay : BYTE;
ucMonth : BYTE;
usYear : SHORT;
END
END;
TIME = RECORD
CASE : BOOLEAN OF
| TRUE:
l : LONG;
ELSE
ucHund : BYTE;
199
Advanced Programming Topics
ucSecond : BYTE;
ucMinute : BYTE;
ucHour : BYTE;
END
END;
Clarion STRINGs are passed in the same manner as Modula-2 open ARRAY OF CHAR parameters with the
call(o_a_copy=>off) pragma in effect (the length and the address of the string are passed).
The following example code declares a string of 20 characters, assigns some data into it and passes it as a parameter
to a Modula-2 procedure
MAP
MODULE('M2_Code')
M2_Write_Proc(*STRING), NAME('M2_Code$M2_Write_Proc')
END
END
StringVar STRING(20)
CODE
StringVar = 'Hello World...'
M2_Write_Proc(StringVar)
Note that Clarion STRINGs are not NULL terminated, they are padded with spaces up to the length of the data buffer.
In the above example, StringVar would be padded with spaces up to a length of 20 characters. Variables of type
CSTRING are automatically NULL terminated when data is assigned to them. This makes it possible for existing
Modula-2 routines to operate on the data.
A Clarion GROUP is roughly equivalent to a Modula-2 RECORD. There are several forms of declaration for Clarion
GROUPs. The following conforms to the Modula-2 declaration of the DATE type above:
DateType GROUP
n LONG
d GROUP,OVER(n)
ucDay BYTE
ucMonth BYTE
usYear SHORT
END
END
200
Advanced Topics
The OVER attribute is used to ensure that n and d occupy the same memory, the total size of the group is the size of
the member n. When passed as parameters, GROUPs are normally passed as three parameters: first, an UNSIGNED
is passed which contains the size of the GROUP; second, the address of the GROUP structure, and third, the address
of a buffer containing a type descriptor for the GROUP. The contents of the type descriptor are not discussed here and
are subject to change in future versions of Clarion. You may use the RAW attribute in your Clarion prototype for the
Modula-2 procedure to instruct the compiler to pass only the address of the GROUP, otherwise you must define your
Modula-2 procedure to take 2 extra parameters:
MAP
MODULE('M2_Code')
M2_Proc1(*GROUP)
M2_Proc2(*GROUP), RAW
END
END
ALIAS
SHORT = INT16;
USHORT = INT16;
LONG = INTEGER;
ULONG = INTEGER;
SREAL = SHORTREAL;
201
Advanced Programming Topics
Clarion DATE and TIME data types may be passed to Pascal procedures as a LONG, the DATE and TIME records can
then be used to resolve the elements of the date or time from the LONG value.
DATE = RECORD
CASE BOOLEAN OF
TRUE:
(n : LONG);
FALSE:
(ucDay : BYTE;
ucMonth : BYTE;
usYear : SHORT);
END;
TIME = RECORD
CASE BOOLEAN OF
TRUE:
(n : LONG);
FALSE:
(ucHund : BYTE;
ucSecond : BYTE;
ucMinute : BYTE;
ucHour : BYTE);
END;
Because of Clarion’s two parameter method of passing STRINGs, the STRING structure is useful for certain internal
uses, but cannot be used to accept parameters from Clarion:
TYPE
STRING = RECORD
usLen : USHORT;
pucString : ^CHAR;
END;
Clarion PSTRINGs are passed by address in the same manner as Pascal STRING parameters with the
call(s_copy=>off) pragma in effect (the length and the address of the string are passed).
202
Advanced Topics
The following example code declares a string of 20 characters, assigns some data into it, and passes it as a parameter
to a Pascal procedure:
MAP
MODULE('Pas_Code')
Pas_Write_Proc(*PSTRING), NAME('Pas_Code$Pas_Write_Proc')
END
END
StringVar PSTRING(20)
CODE
StringVar = 'Hello World...'
Pas_Write_Proc(StringVar)
A Clarion GROUP is roughly equivalent to a Pascal RECORD. There are several forms of declaration for Clarion
GROUPs. The following duplicates the Pascal declaration of the DATE type above:
DateType GROUP
n LONG
d GROUP,OVER(n)
ucDay BYTE
ucMonth BYTE
usYear SHORT
END
END
203
Advanced Programming Topics
The OVER attribute is used to ensure that n and d occupy the same memory, the total size of the group is the size of
the member n. When passed as parameters, GROUPs are normally passed as three parameters: first, a USHORT is
passed which contains the size of the GROUP; second, the address of the GROUP structure; and third, the address of
a buffer containing a type descriptor for the GROUP. The contents of the type descriptor are not discussed here and
are subject to change in future versions of Clarion. You may use the RAW attribute in your Clarion prototype for the
Pascal procedure to instruct the compiler to pass only the address of the GROUP, otherwise you must define your
Pascal procedure to take 2 extra parameters:
MAP
MODULE('Pas_Code')
Pas_Proc1(*GROUP)
Pas_Proc2(*GROUP), RAW
END
END
204
Advanced Topics
205
Advanced Programming Topics
Since the Clarion language does not have a signed BYTE data type, linker warnings (‘type inconsistency’) will result
when you prototype a function which receives a char parameter. As long as you are aware that the C function is
expecting a signed value, and correctly adjust the BYTE field’s bitmap to pass a value in the range -128 to 127, this
warning may be safely ignored.
The RAW attribute must be used when a C function expects to receive the address of a CSTRING or GROUP
parameter. By default, Clarion STRING, CSTRING, PSTRING, and GROUP parameters are passed (internally) to
other Clarion procedures as both the address and length of the string. C functions do not usually want or need the
length, and expect to receive only the address of the data. Therefore, the RAW attribute overrides this default.
If the C function returns void, there is no data returned and the function fits the definition of a Clarion PROCEDURE. If
the C function does return data, it is prototyped with the actual data type returned and the function fits the definition of
a Clarion PROCEDURE that returns a value and may be called as part of a condition, assignment, or parameter list.
206
Advanced Topics
int SHORT
short SHORT
long LONG
float SREAL
double REAL
int * *SHORT
short * *SHORT
long * *LONG
float * *SREAL
double * *REAL
As you can see, the Clarion return type for a char * is CSTRING (not *CSTRING as you might expect). This is because
the Clarion compiler automatically dereferences the pointer to the data when the function returns (as it does with all the
pointer return types).
207
Advanced Programming Topics
Notice that the Clarion return data type for struct * is ULONG. This will generate a "type inconsistency" linker warning.
This occurs because the Clarion language does not use pointers, and the ULONG is a four-byte integer which can serve
as a replacement for a pointer return type. The warning is not a problem and can be safely ignored. You would probably
use memcpy() to get at the returned data.
Passing Parameters
Clarion offers two distinct methods of passing parameters to functions or procedures: "passed by value" and "passed by
address."
"Passed by value" means that the calling code passes a copy of the data to the called function or procedure. The called
code can then operate on the data without affecting the caller’s copy of the data. These parameters are specified by the
parameter’s data type in the prototype.
"Passed by address" means that the calling code passes the address of the data to the called function or procedure. With
this method, the called function or procedure can modify the caller’s data. These parameters are specified by prefixing the
parameter’s data type with an asterisk (*) in the prototype:
MAP
MODULE('My_C_Lib')
Var_Parameter(*USHORT) ! Parameter passed by address
Val_Parameter(USHORT) ! Parameter passed by value
END
END
These declarations represent the Clarion interface to the functions contained in the C library My_C_Lib. The following
example are the equivalent C declarations:
MAP
MODULE('My_C_Lib')
StdC_Conv(UNSIGNED, ULONG), C, NAME('_StdC_Conv')
StdPascal_Conv(UNSIGNED, ULONG), PASCAL, NAME('STDPASCAL_CONV')
END
END
When the StdC_Conv procedure is called, the ULONG parameter is pushed on the stack followed by the UNSIGNED
parameter. When StdPascal_Conv is called, the UNSIGNED parameter is pushed followed by the ULONG parameter.
You should be very careful that calling conventions match, otherwise the program may behave unpredictably. When
interfacing with code produced by SoftVelocity compilers, the C and PASCAL calling convention attributes are not
necessary because Clarion uses the SoftVelocity register-based calling conventions.
When writing SoftVelocity C functions to be called from a Clarion program, the CLA_CONV macro (discussed above)
should be used to select the correct naming conventions. The best way of achieving this is to declare any interface
functions in a separate header (.H) file and to apply the conventions to these declarations. C++ functions must be
declared using "Pascal" external linkage (also discussed above). Modula-2 and Pascal naming conventions are best
handled by using the NAME attribute on the prototype.
209
Advanced Programming Topics
210
Advanced Topics
the form: MyModule$MyProcedure. The way to resolve these differences is to use Clarion’s NAME attribute to specify the
full name of the Modula-2 or Pascal procedure to the Clarion compiler:
MAP
MODULE('M2_Code')
M2_Proc1(*GROUP), RAW, NAME('M2_Code$M2_Proc2')
END
MODULE('Pas_Code')
Pas_Proc1(*GROUP), RAW, NAME('Pas_Code$Pas_Proc2')
END
END
The corresponding Modula-2 definition module might be:
DEFINITION MODULE M2_Code;
TYPE
GROUP = RECORD
(* Members *)
END;
PROCEDURE M2_Proc1(VAR Data: GROUP);
END M2_Code.
The corresponding Pascal interface unit might be:
INTERFACE UNIT Pas_Code;
TYPE
GROUP = RECORD
(* Members *)
END;
PROCEDURE Pas_Proc1(VAR Data: GROUP);
END.
The naming conventions used by Clarion for data differ from those used for PROCEDURES, and are more complex.
Therefore, the NAME() attribute should be used to generate a Modula-2 or Pascal-compatible name for any Clarion data
that needs to be accessed between languages. Modula-2 and Pascal data names are case sensitive and prefixed with the
name of the module and a ‘@’ in the form: MyModule@MyProc.
The EXTERNAL and DLL Attributes
The EXTERNAL attribute is used to declare Clarion variables and functions that are defined in an external library. The
DLL attribute declares that an EXTERNAL variable or functions is defined in a Dynamic Link Library (DLL).
211
Advanced Programming Topics
These attributes provide Clarion programs with a means of accessing public data in external libraries. The compiler will
not reserve space for any variables declared as EXTERNAL. For example:
typedef struct {
unsigned long ul1;
unsigned long ul2;
} StructType;
#ifdef __cplusplus
extern "C" { /* Use C naming conventions, which will require use */
#endif /* of the NAME attribute in the Clarion prototype */
StructType Str1; /* Define Str1 */
StructType Str2; /* Define Str2 */
#ifdef __cplusplus
} /* Restore C++ conventions */
#endif
The following Clarion declarations are all that is necessary to make Str1 and Str2 available to Clarion programs.
StructType GROUP,TYPE ! Declare a user defined type
ul1 ULONG
ul2 ULONG
END
! Declare Str1 and Str2 which are defined in the C module
Str1 LIKE(StructType),NAME('_Str1'),EXTERNAL
Str2 LIKE(StructType),NAME('_Str2'),EXTERNAL
The NAME attribute is used to allow the linker to use the C naming convention when referencing Str1 or Str2.
212
Advanced Topics
Programming Considerations
The following code does nothing more than provide entry points for the Clarion code to access the functionality of the
DIRLIST class library. Since Clarion performs no name-mangling and cannot access classes or their members, this API is
necessarily fairly simple.
The following is the corresponding MAP structure prototype to allow Clarion to call the MakeFileList interface function:
213
Advanced Programming Topics
MAP
MODULE('DirList')
MakeFileList(*CSTRING,USHORT,USHORT),RAW,NAME('_MakeFileList')
END
END
One disadvantage of this is that, given a large class library, it appears to involve a lot of extra work to create a suitable
interface. In practice, however, it should only be necessary to provide a very small interface to begin taking advantage of
an existing C++ class library.
It is not possible to call C++ code compiled using non-SoftVelocity C++ compilers from a Clarion application. C++
modules usually require special initialization — constructors for all static objects must be invoked in the correct order. This
initialization process must be performed by the Clarion start-up code. Clarion’s startup code automatically performs the
necessary initialization for any SoftVelocity C++ modules that are present, but it will not initialize modules compiled with
other C++ compilers. Even if the modules did not require initialization, other C++ compilers use different calling and
naming conventions, and adopt different internal class structures. This makes it impossible to use C++ class libraries in
Clarion applications compiled with a compiler other than SoftVelocity C++.
Summary:
The Clarion API provides a number of features to assist developers who need to interface to code written in other
programming languages. With a little care, it is possible to create Clarion interfaces to some extremely powerful external
libraries.
When preparing interfaces to libraries written in other languages you should consider the following suggestions:
* Don’t write C, C++, Pascal, or Modula-2 functions to return CSTRING variables to Clarion. Have the other language
routine place the CSTRING value in a public variable, or pass a *CSTRING (by address) parameter to the C
routine to receive the value.
* Don’t call Clarion procedures that return STRING variables from other language functions. Have the Clarion procedure
place the return value in a public variable or pass a *CSTRING (by address) parameter to the other language
procedure.
* For simplicity and efficiency, STRING and GROUP parameters should usually be passed by address with the RAW
attribute to ensure only the address is passed.
* Test the application in XLARGE memory model first.
215
Advanced Programming Topics
Threading Model
The thread model uses preemptive threads. Typical Clarion programs won’t require more than a "compile and link" to see
its benefits.
Some advantages of this model are:
It is much easier to access COM objects
You can have threads running independently of other threads.
Programs are more stable.
This thread model also makes the OLE layer much easier to work with because the object will run on the Clarion thread
whereas currently it is run on its own separate thread.
216
Advanced Topics
217
Advanced Programming Topics
Steps (1) and (2) can be required if FILEs or VIEWs use strings that are imported from external
DLLs.
218
Advanced Topics
The launching thread will continue until it encounters the ACCEPT statement. Upon execution of the ACCEPT statement:
10. RTL resumes the launched thread.
11. RTL calls the entry point of the ThreadProc.
Therefore, a launched thread will remain suspended until the next call to ACCEPT from the launching thread. Only
initialization and constructors for threaded variables are executed.
The use of RESUME with the START statement immediately executes Step 10 above without waiting for the call to
ACCEPT. In other words, use of RESUME with START does not depend on the ACCEPT statement for resuming thread
execution. This allows a new thread to be started from windowless threads.
The same can be said by using the SUSPEND statement immediately after START, e.g., SUSPEND immediately stops
thread execution and does not wait for the ACCEPT loop.
219
Advanced Programming Topics
If you need access to threaded data or to make calls to Clarion functions from a non-Clarion thread you must use the
AttachThreadToClarion() function from the non-Clarion DLL. On the call to AttachThreadToClarion(), instances of
threaded variables are allocated and their constructors are called, and so they are available to the thread created by the
CreateThread API.
Prototype Information
Clarion prototype:
AttachThreadToClarion (BOOL),PASCAL
C/C++ prototype:
void PASCAL AttachThreadToClarion (int);
This AttachThreadToClarion function must be called by the launching thread to attach it to the thread handler in the
runtime library. Without this call, any attempt within the thread to use thread dependent Clarion functions and operations
(all string and decimal operators, BIND/EVALUATE, ERROR/ERRORCODE, most FILE statements, etc.) will cause a
program termination.
The main thread of Clarion programs and all threads started with the START function do not require this call.
The AttachThreadToClarion has a BOOLEAN parameter that is used in the following manner. If
AttachThreadToClarion is called with a FALSE ( 0 ) parameter, the runtime library does not allocate instances of
threaded variables for this thread and hence, does not initialize them. If the parameter is TRUE ( 1 ), the runtime library
allocates instances of threaded variables for the thread and executes the initialization code for them. Any attached
threads are detached automatically on their termination.
220
Advanced Topics
This topic is useful for developers looking to migrate their applications from Clarion 5.5 or earlier, and who wish to
understand the new preemptive thread model. Be sure to read the "Multi-Threaded Programming" PDF for a more
detailed look at the changes, and information on how to implement thread synchronization.
LOOP
MyVar = THREAD()
IF MyVar ~= THREAD()
MESSAGE(‘Boom’)
END
END
Accessing COM objects and some Windows API functions require careful coding and the use of LOCKTHREAD and
UNLOCKTHREAD.
Preemptive Threads
Preemptive threads run independently of each other. Only one thread at a time has focus, but focus can change from one
line of code to the next. The switching between threads is under the control of the OS.
Advantages of Preemptive Threads
Have you ever built an application where users had to wait while the application performed some lengthy calculation or
operation? Then you can benefit from a preemptive thread model.
Understanding threads using a modern operating system's multi-threading capabilities properly, is a fundamental step
toward creating fast, responsive applications. Clarion makes creating multi-threaded applications easier than ever.
221
Advanced Programming Topics
To understand the power of multithreading you need to know something about how the Windows operating system works
under the hood. Windows is a preemptive multitasking operating system. The system CPU can do only one thing at a
time, but to give the illusion that multiple processes are running simultaneously the operating system splits the CPU time
between the various running processes.
The term preemptive means that the operating system determines when each task executes and for how long.
Preemptive multitasking prevents one task from taking up all the processor's time.
The operating system allocates small "slices" of CPU time to each of these processes. Because the CPU is very fast, and
the time slices can be extremely small, processes appear to run simultaneously. On a multi-processor system things are a
bit more complicated but the basic idea is the same—the operating system divides the time of the CPUs among the
processes that need it.
Second, programming with multiple threads can be complex. Creating a single extra thread to handle some background
calculations is fairly straightforward, but implementing many threads is a demanding task and can be the source of many
hard-to-find bugs. A best practices approach to these potential problems is to use multithreading only when it provides a
clear advantage, and then to use a few threads as possible.
Third is the question of shared resources. Because they're running in the same process, the threads of a multi-threaded
program have access to that process's resources, including global, static, and instance fields. Also, threads may need to
share other resources such as communications ports and file handles. You must synchronize the threads in most multi-
threaded applications to prevent conflicts when accessing resources, such as deadlocks (when two threads stop as each
waits for the other to terminate).
You need to be very careful when accessing non-threaded global variables. The following code will generate the
message intermittently when using preemptive threads.
LOOP
MyVar = THREAD()
IF MyVar ~= THREAD()
MESSAGE(‘Bang’)
END
END
222
Advanced Topics
You can protect yourself in this situation by using the IMutex interface declared in CWSYNCH.INT to stop other threads
executing at the same time (for a short time)
If you do not protect yourself when accessing global non-threaded variables you will have an application that will appear
to work, but will occasionally do something strange that is hard to reproduce with any consistency.
Programming with preemptive threads can be complex! Even code as simple as…
MyFunc FUNCTION()
MyResponse = RequestCompleted
RETURN
MyFunc()
IF MyResponse = RequestCompleted
!Do Something
END
…may not work how you expected with multiple threads active, if MyResponse does not have the THREAD attribute
223
Advanced Programming Topics
Conclusion
You can now take advantage of the power and flexibility of preemptive threads. However, doing so requires that you are
very careful with accessing global non-threaded variables.
Be sure to read the "Multi-Threaded Programming" PDF for a more detailed look at the changes, and information on
how to implement thread synchronization.
224
Advanced Topics
Multi-Threading Programming
Multi-Threading Programming
Contents:
Overview
Code differences from versions prior to version 6
Coding Techniques for Preemptive Multi-Threading
Thread Synchronization
Synchronization Objects
Summary of Synchronization Objects Properties and Methods
Synchronization Object Interface Methods Summary
225
Advanced Programming Topics
Overview
This topic introduces programming techniques you implement when you program in a preemptive multi-threading
environment. It identifies the type of code and objects that require attention, and also provides code examples for adding
thread synchronization to properly handle access to shared non-threaded data.
Throughout this topic there are references to static variables. There are three ways of creating a variable in Clarion such
that it is static:
The variable is declared in Global Data and does not have the THREAD attribute;
The variable is declared in Module Data and does not have the THREAD attribute;
The variable is declared in Procedure Data and has the STATIC attribute, but does not have the THREAD
attribute;
Static variables are potentially dangerous in a preemptive environment, and should be avoided where possible. Later
sections in this topic will describe what to do when they cannot be avoided.
What is a threading model?
A threading model describes how different threads work together under a multi-threading operating system. There are two
types of threading models: cooperative threading and preemptive threading.
Under the cooperative threading model an application could have multiple threads ‘running’ at the same time. But only
one of these threads could do anything at any point in time. For another thread (or program) to get a chance to do
anything the current thread had to relinquish control of the PC back to the operating system. The operating system would
then choose which thread would next have a chance to do something. Thus the threads cooperate with each other and
the operating system to give each other a chance to do something. This was easy to program, but it meant that programs
could behave badly and take full control of the PC locking out other programs.
When Windows NT arrived so did a new style of threading: Preemptive multi-threading. Now threads could run
simultaneously, either on separate CPUs or by having the operating system suspend one thread and start another
whenever the operating system wanted. This released the power of multi-processor machines and stopped the problem of
badly behaved programs. But it was a much harder paradigm to program with.
Threads Pre-Clarion 6
Clarion for Windows was designed to make programming easy. Therefore the problems of preemptive threading were
considered unnecessarily complex for the rewards. Clarion threads were not cooperative by their nature. They were
standard Win32 threads with explicit synchronization implemented in the RTL to emulate cooperative behavior.
The emulation of cooperative threads made it easy to use variables that were global in scope (can be used anywhere in
your program), but whose contents were different depending on what thread was active; for example a global variable with
the THREAD attribute. To do this some mechanism was required to make sure that the contents changed each time a
different thread became active. Due to the cooperative nature of Clarion threads the RTL was responsible to swap the
contents of the threaded variables every time a thread gained or lost focus.
This made it very easy to program, but added the restriction that only one Clarion thread could be active at any time
Threads in Clarion
Clarion supports the preemptive thread model. To do this, the automatic data swapping that was done by the RTL had to
end. The operating system determines when a thread receives a CPU time slice. So there is no opportunity for the RTL to
swap the contents of variables.
Although Clarion now supports preemptive multi-threading, it does not mean you have to suddenly rewrite all your
programs. If you follow the guidelines set out in this paper for avoiding the use of global variables whenever possible and
using the synchronization techniques described, you’ll be able to deliver new capabilities to your end-users.
226
Advanced Topics
AFile FILE,DRIVER(‘TopSpeed’),THREAD
Then in your EXE you would declare the file as
AFile FILE,DRIVER(‘TopSpeed’),EXTERNAL
Now a variable with the THREAD attribute has different memory allocated for each thread. The compiler generates code
to make sure that the right memory is accessed regardless of which thread is running. For the compiler to generate the
right code it needs to know if a variable has the THREAD attribute regardless of where the variable is defined. So in now
if you have a file definition in a DLL you declare it as
AFile FILE,DRIVER(‘TopSpeed’),THREAD
Then in your EXE you declare the file as
Notice the THREAD attribute is present in both declarations, unlike previous versions.
227
Advanced Programming Topics
In version prior to Clarion 6, taking the ADDRESS() of a threaded variable would always return the same result regardless
of what thread is running. Now every thread has its own memory location for a threaded variable. So ADDRESS() no
longer will return the same value. If you need a constant address for a threaded global variable you can use the new
function INSTANCE() and pass a Thread Number of 0. For example, to get a unique identifier for a threaded FILE that you
can be certain will be the same regardless of what thread you are on you used to do ADDRESS(MyFile). You now do
INSTANCE(MyFile, 0)
228
Advanced Topics
If the value to be protected is a LONG (and not in a GROUP) you are probably safe in not synchronizing most thread
accesses, since reads/writes happen in only one operation. If a LONG is defined in a GROUP/RECORD/QUEUE then it
might not be aligned data and require more then one operation. In summary a LONG has fewer risks of corrupt half
updated data then strings, but still have some risks when multiple commands are involved.
See the Synchronization Objects section below for descriptions and examples on how to use these objects.
IMPORTANT!
It is very important that you lock out other threads for as short a time as possible. Ideally, copying the data
you need into thread safe variables then releasing the lock. You should not have any user input inside a piece
of code that locks other threads.
To guarantee that your static variables are protected it is best to move them into a static class that has read and write
methods for manipulating them. Make them PRIVATE, create Get and Set methods, and have synchronization code to
protect and manipulate them.
Changes to Common Coding Practices
Using static variables to pass parameters
If you are using a static variable to pass values to a started procedure on a new thread, change the prototype of that
procedure so the values it requires are passed as parameters. Both the ABC and Clarion template chains now support
this feature.
229
Advanced Programming Topics
Thread 1 Thread 2
GET(Queue, 1)
GET(Queue, 2)
Assign Queue Data to local var
Assign Queue Data to local var
Thread 1 gets a time slice from the OS and reads from the Queue, then the OS give thread 2 a time slice before thread 1
makes the assignment of the Queue data to a local variable. Now both thread 1 and thread 2 ends up reading the
information for element 2 of the Queue. To avoid this situation you need to synchronize access to the queue’s buffer. To
keep the time that other threads are locked out while the queue is accessed to a minimum, you should read from the
queue and assign the data from the queue’s buffer into a threaded memory buffer. See the section below on
ICriticalSection for a complete example showing how to do this.
GlobalQ QUEUE,THREAD
Data STRING(10)
END
QLock &ICriticalSection
GlobalQueuePopulator CLASS,THREAD
Construct PROCEDURE
Destruct PROCEDURE
END
GlobalQueuePopulator.Construct PROCEDURE
BaseQ &GlobalQ
recs UNSIGNED,AUTO
i UNSIGNED,AUTO
230
Advanced Topics
CODE
IF THREAD() <> 1
QLock.Wait()
BaseQ &= INSTANCE(GlobalQ, 1)
recs = RECORDS(BaseQ)
LOOP i = 1 TO recs
GET(BaseQ, i)
GlobalQ.Data = BaseQ.Data
ADD(GlobalQ)
END
QLock.Release()
ELSE
QLock &= NewCriticalSection()
END
GlobalQueuePopulator.Destruct PROCEDURE
CODE
IF THREAD() = 1
QLock.Kill()
END
The WAIT function will wait until no other threads want the object. It then takes hold of the object until a subsequent call to
Release. Other threads that call the WAIT function will wait indefinitely until the other thread releases the object.
Proper programming techniques are essential here to avoiding a "deadly embrace", or deadlock.
SyncObj1.Wait
SyncObj2.Wait
SyncObj2.Wait
SyncObj1.Wait
Deadlocks should not be a big issue if you make sure you have a hierarchy of synchronization objects. Ideally, you only
use one object at a time, but if you must use multiple objects, always acquire a lock on the top synchronization object first,
then the other one. That way, you can never get into the aforementioned scenario.
231
Advanced Programming Topics
Thread Synchronization
The Clarion runtime has a variety of built in interfaces and procedures to help you maintain synchronization between your
threads. The POST and EVENT functions have always been in the Clarion language for thread synchronization.
SUSPEND and RESUME functions allow you to stop and start another thread, INSTANCE allows you to get another
thread’s contents for a variable and the ICriticalSection, IMutex, ISemaphore and IReaderWriterLock are interfaces to
objects that allow you to synchronize the processing between multiple threads and also multiple processes.
POST/EVENT
You have always been able to synchronize thread processing by posting an event from one thread to another using
POST() to send the event and EVENT() to receive it. See the SUSPEND/RESUME section below for an example on using
these functions to synchronize two threads.
SUSPEND/RESUME
SUSPEND allows you to stop another process. RESUME starts that process again. You can issue multiple SUSPEND
calls for a thread. The same number of RESUME calls must be made for that thread to restart.
The SUSPEND procedure suspends a thread specified by the threadno parameter. If the threadno parameter is a number
of an active thread, its execution is suspended and a suspending counter is incremented. Each additional SUSPEND
statement issued to the same active thread will increment the suspending counter by one. Therefore, a thread that has
been suspended with a given number of SUSPEND statements can only resume thread execution when an equal number
of RESUME statements has been executed.
EXTREME CAUTION should be taken with MDI programs using SUSPEND, as improper use can cause program lockups.
All MDI child windows have an MDI client window as a parent, and the MDI client window can send rather than post
messages to its child windows.
For example, calling the inter-thread SendMessage modal function causes the calling thread (the MDI client window) to
suspend activity until the called thread (the MDI Child window) returns from the call. If the called thread is suspended, we
would have a program lockup.
The SUSPEND and RESUME functions can be very useful for controlling threads that are CPU intensive. For example,
rebuilding keys on a file. Here is an example program that starts a BUILD of a file and allows the user to pause the
build and restart it.
PROGRAM
MAP
DoBuild(STRING)
END
MyFile FILE,DRIVER('TopSpeed'),PRE(F)
Key1 KEY(F:Field1),PRIMARY
Key2 KEY(F:Field2)
Key3 KEY(F:Field3, Field4)
RECORD
Field1 LONG
Field2 STRING(20)
Field3 STRING(20)
Field4 STRING(20)
END
END
232
Advanced Topics
AllDone EQUATE(500H)
Building BYTE
ThreadID SIGNED,AUTO
CODE
OPEN(BuilderWin)
ThreadID = START(DoBuild, , THREAD())
Building = TRUE
ACCEPT
CASE EVENT()
OF AllDone
MESSAGE('Build Complete')
BREAK
OF Event:Accepted
IF ACCEPTED() = ?Button
IF Building
SUSPEND(ThreadID)
?Button{PROP:Text} = 'Resume Building'
ELSE
RESUME(ThreadID)
?Button{PROP:Text} = 'Suspend Build'
END
END
END
END
233
Advanced Programming Topics
INSTANCE
In versions of Clarion prior to Clarion 6.0 a variable’s memory location was constant regardless of which thread accessed
the variable. Therefore this code would always work:
PROGRAM
MAP
AFunc()
END
GlobVar SIGNED,THREAD
Addr LONG
CODE
Addr = ADDRESS(GlobVar,1)
START(AFunc)
AFunc PROCEDURE
CODE
IF Addr <> ADDRESS(GlobVar)
MESSAGE('Panic')
END
This sort of code was used in ABFILE.CLW to make sure the file manager matched the file it was meant to manage. To
allow programs to know what variable they are really using you can now use the INSTANCE function to get the address of
the variable on any thread, and most importantly on thread 1. The above code would need to be modified as follows to
work in Clarion 6.0.
PROGRAM
MAP
AFunc()
END
GlobVar SIGNED,THREAD
Addr LONG
CODE
Addr = ADDRESS(GlobVar)
START(AFunc)
AFunc PROCEDURE
CODE
IF Addr <> INSTANCE(GlobVar,0)
MESSAGE('Panic')
END
234
Advanced Topics
Synchronization Objects
A Synchronization Object is an object used to control how multiple threads cooperate in a preemptive environment. There
are four Synchronization Objects supplied by the Clarion runtime: Critical Sections (ICriticalSection), Mutexes (IMutex),
Semaphores (ISemaphore), and Read/Write Locking (IReaderWriterLock).
Clarion makes it very easy to take advantage of preemptive threads in your applications. All template generated code
uses threaded objects to ensure proper behavior. When you embed code that works with threaded data you don’t have
any worries, but when you access shared non-threaded data you should use a synchronization object.
Due to the fact that Windows uses procedure-modal methods when dealing with MDI based applications
(a program with an APPLICATION window) you must not have any user input when you have control of a
synchronization object with an MDI based application. This is likely to lead to your application locking up
If you must have user input, then you must release control of the synchronization object while waiting for
user input.
IcriticalSection and CriticalSection
Use an ICriticalSection when you want only one thread accessing some resource (e.g. a global, non-threaded variable) at
any one time. An ICriticalSection is faster than an IMutex. If you do not need the extra features of an IMutex, use an
IcriticalSection
CriticalSection is a built-in class that allows for easy creation of simple, global synchronization objects.
Following are a couple of examples that make sure that only one thread is accessing a static queue at a time.
PROGRAM
QueueData GROUP,THREAD
ThreadID LONG
Information STRING(20)
END
!Include CriticalSection
INCLUDE(‘CWSYNCHC.INC’),ONCE
MAP
WriteToQueue()
WriteToQueue(*QueueData)
235
Advanced Programming Topics
ReadFromQueue()
ReadFromQueue(*QueueData)
END
NonThreadedQueue QUEUE
Data LIKE(QueueData)
END
QueueLock CriticalSection
CODE
! Do everything
WriteToQueue PROCEDURE()
! Assumes QueueData is used to pass data. This is thread safe
! because QueueData has the THREAD attribute
CODE
QueueLock.Wait() !Lock access to NonThreadedQueue.
NonThreadedQueue.Data = QueueData
GET(NonThreadedQueue, NonThreadedQueue.Data.ThreadId)
IF ERRORCODE()
ADD(NonThreadedQueue)
ELSE
PUT(NonThreadedQueue)
END
QueueLock.Release() !Allow other access to the queue
236
Advanced Topics
END
QueueLock.Release() !Allow other access to the queue
ReadFromQueue PROCEDURE()
! Returns results in QueueData. This is thread safe
! because QueueData has the THREAD attribute
CODE
QueueLock.Wait() !Lock access to NonThreadedQueue.
NonThreadedQueue.Data.ThreadId = THREAD()
GET(NonThreadedQueue, NonThreadedQueue.Data.ThreadId)
QueueData = NonThreadedQueue.Data
QueueLock.Release() !Allow other access to the queue
The previous example suffers from the problem that anyone can access the global queue and they are not forced to use
the matching QueueLock critical section. The following example removes this problem by moving the non-threaded queue
and the critical section into a static class.
PROGRAM
QueueData GROUP,THREAD
ThreadID LONG
Information STRING(20)
END
NonThreadedQueue QUEUE,TYPE
Data LIKE(QueueData)
END
QueueAccess CLASS
237
Advanced Programming Topics
QueueData &NonThreadedQueue,PRIVATE
QueueLock &ICriticalSection,PRIVATE
Construct PROCEDURE
Destruct PROCEDURE
WriteToQueue PROCEDURE
WriteToQueue PROCEDURE(*QueueData)
ReadFromQueue PROCEDURE()
ReadFromQueue PROCEDURE(*QueueData)
END
INCLUDE(‘CWSYNCHC.INC’)
MAP
END
CODE
! Do everything
QueueAccess.Construct PROCEDURE
CODE
SELF.QueueLock &= NewCriticalSection()
SELF.QueueData &= NEW(NonThreadedQueue)
QueueAccess.Destruct PROCEDURE
CODE
SELF.QueueLock.Kill()
DISPOSE(SELF.QueueData)
QueueAccess.WriteToQueue PROCEDURE()
! Assumes QueueData is used to pass data. This is thread safe
! because QueueData has the THREAD attribute
CODE
SELF.QueueLock.Wait() !Lock access to NonThreadedQueue.
SELF.QueueData.Data = QueueData
GET(SELF.QueueData, SELF.QueueData.Data.ThreadId)
IF ERRORCODE()
ADD(SELF.QueueData)
ELSE
PUT(SELF.QueueData)
238
Advanced Topics
END
SELF.QueueLock.Release() !Allow other access to the queue
QueueAccess.ReadFromQueue PROCEDURE()
! Returns results in QueueData. This is thread safe
! because QueueData has the THREAD attribute
CODE
SELF.QueueLock.Wait() !Lock access to NonThreadedQueue.
SELF.QueueData.Data.ThreadId = THREAD()
GET(SELF.QueueData, SELF.QueueData.Data.ThreadId)
QueueData = SELF.QueueData.Data
SELF.QueueLock.Release() !Allow other access to the queue
IWaitableSyncObject
The IWaitableSyncObject is the base interface for IMutex and ISemaphore. This allows you to create procedures that
work with either type of synchronization object without requiring the procedure to know exactly what type of object it is.
239
Advanced Programming Topics
IMutex
An IMutex is used when you want to allow only one thread to access a resource. Just like an ICriticalSection. However,
IMutexes have the added features of being able to not only synchronize threads, but also synchronize different processes.
Thus, if you have a resource that can only have one process accessing it at one time (e.g. a registration file that controls
access to multiple programs) then you will need to use an IMutex that is created by calling NewMutex(Name). Name must
be the same for all processes that use it to access the same
set of shared resources.
Another time you would use an IMutex rather than an ICriticalSection is if you do not want to always lock a thread.
Finally, an IMutex works better than an ICriticalSection in MDI applications, as ICriticalSection objects may cause
deadlocks. Orphaned mutexes are killed by the operating system after a brief time, but CriticalSections are not. Deadlocks
in CriticalSections are usually caused by a programming error (e.g., calling Wait before a form procedure and Release
after it).
A Mutex is a very simple way to limit the user to having one instance of your program running at any time. The following
example shows the use of the Name parameter for creating a Mutex and the TryWait method to limit your program in this
way.
PROGRAM
INCLUDE('CWSYNCHM.INC'),ONCE
MAP
END
Limiter &IMutex,AUTO
Result SIGNED,AUTO
LastErr LONG,AUTO !<< return error
CODE
Limiter &= NewMutex('MyApplicationLimiterMutex',,LastErr)
IF Limiter &= NULL
MESSAGE ('ERROR: Mutex can not be created ' & LastErr)
ELSE
Result = Limiter.TryWait(50)
IF Result <= WAIT:OK
!Do Everything
Limiter.Release() !release
ELSIF Result = WAIT:TIMEOUT
MESSAGE('Timeout')
ELSE
MESSAGE('Waiting is failed ' & Result) !show Result
END
Limiter.Kill()
END
The difference between an IMutex and an ISemaphore is an IMutex can only have one thread successfully wait. An
ISemaphore can have multiple threads successfully wait. It is also possible to create a semaphore where no thread can
successfully wait.
240
Advanced Topics
PROGRAM
INCLUDE('CWSYNCHM.INC')
INCLUDE('CWSYNCHC.INC')
INCLUDE('ERRORS.CLW')
MAP
Reader()
Writer()
END
LogFile FILE,DRIVER('ASCII'),CREATE,NAME('LogFile.txt'),THREAD
RECORD
Line STRING(255)
END
END
AccessToGlobals CriticalSection
NewData Semaphore
LimitReaders &ISemaphore
GlobalStrings QUEUE,PRE(Q)
Data STRING(50)
END
241
Advanced Programming Topics
AppFrame APPLICATION('Reader/Writer'),AT(,,400,240),SYSTEM,MAX,RESIZE
MENUBAR
MENU('&File'),USE(?FileMenu)
ITEM('E&xit'),USE(?Exit),STD(STD:Close)
END
MENU('&Launch'),USE(?LaunchMenu)
ITEM('Reader'),USE(?LaunchReader)
ITEM('Writer'),USE(?LaunchWriter)
END
END
END
CODE
LimitReaders &= NewSemaphore(1)
SHARE(LogFile)
IF ERRORCODE() = NoFileErr
CREATE(LogFile)
SHARE(LogFile)
END
IF ERRORCODE()
STOP('Log File could not be opened. Error: ' & ERROR())
END
OPEN(AppFrame)
ACCEPT
IF EVENT() = EVENT:Accepted
CASE ACCEPTED()
OF ?LaunchReader
START(Reader)
OF ?LaunchWriter
START(Writer)
END
END
END
! Test to see if Reader is still alive
IF LimitReaders.TryWait(1) = WAIT:TIMEOUT
!It is, so lets kill it
AccessToGlobals.Wait()
Q:Data = 'exit'
242
Advanced Topics
ADD(GlobalStrings)
AccessToGlobals.Release()
NewData.Release() ! Release the reader that is waiting
LimitReaders.Wait() ! Wait for thread to terminate
LimitReaders.Release()
END
LimitReaders.Kill()
Reader PROCEDURE
CODE
! Check that there are no other readers
IF LimitReaders.TryWait(1) = WAIT:TIMEOUT
MESSAGE('Only One Reader Allowed at a time. ' &|
'Kill reader by typing ''exit'' in a sender')
RETURN
END
! If TryWait succeeds, then we have control of
! the LimitReaders Semaphore
SHARE(LogFile)
l1 LOOP
NewData.Wait() !Wait until a writer signals that there is some data
AccessToGlobals.Wait()
LOOP
GET(GlobalStrings,1)
IF ERRORCODE() THEN BREAK.
IF Q:Data = 'exit' THEN
FREE(GlobalStrings)
AccessToGlobals.Release()
BREAK l1
ELSE
LogFile.Line = Q:Data
ADD(LogFile)
END
DELETE(GlobalStrings)
END
AccessToGlobals.Release()
END
CLOSE(LogFile)
243
Advanced Programming Topics
Writer PROCEDURE
LocString STRING(50)
Window WINDOW('Writer'),AT(,,143,43),GRAY
PROMPT('Enter String'),AT(2,9),USE(?Prompt1)
ENTRY(@s50),AT(45,9,95,10),USE(LocString)
BUTTON('Send'),AT(2,25,45,14),USE(?Send)
BUTTON('Close'),AT(95,25,45,14),USE(?Close)
END
CODE
OPEN(Window)
Window{PROP:Text} = 'Writer ' & THREAD()
ACCEPT
IF EVENT() = EVENT:Accepted
CASE ACCEPTED()
OF ?Send
AccessToGlobals.Wait()
Q:Data = LocString
ADD(GlobalStrings)
AccessToGlobals.Release()
NewData.Release() ! Release the reader that is waiting
OF ?Close
BREAK
END
END
END
RETURN
IReaderWriterLock
An IReaderWriterLock can be used to allow multiple threads to read from a global resource, but for only one thread to
write to it. No reader is allowed to read the resource if someone is writing and no one can write to the resource if anyone
is reading.
An example of this would be where the user specifies screen colors. This is stored in an INI file and read on startup. As
the user can change these at any time, the code that changes the values needs to obtain a write lock and all those that
read the color information need to obtain a read lock.
244
Advanced Topics
Static queues cannot be synchronized via an IReaderWriterLock because reading a queue also modifies its position. All
queue access must be considered to cause writes.
Here is some simple code that reads and writes to some static variables. To make sure that no one accesses the
statics outside the locking mechanism, the variables are declared as PRIVATE members of a static class.
PROGRAM
INCLUDE('CWSYNCHM.INC'),ONCE
MAP
END
GlobalVars CLASS
AccessToGlobals &IReaderWriterLock,PRIVATE
BackgroundColor LONG,PRIVATE
TextSize SHORT,PRIVATE
Construct PROCEDURE
Destruct PROCEDURE
GetBackground PROCEDURE(),LONG
PutBackground PROCEDURE(LONG)
GetTextSize PROCEDURE(),SHORT
PutTextSize PROCEDURE(SHORT)
END
CODE
GlobalVars.Construct PROCEDURE
CODE
SELF.AccessToGlobals &= NewReaderWriterLock()
GlobalVars.Destruct PROCEDURE
CODE
SELF.AccessToGlobals.Kill()
GlobalVars.GetBackground PROCEDURE()
ret SHORT,AUTO
Reader &ISyncObject
CODE
! You need to copy the static variable
! to somewhere safe (A local variable) which
! can then be returned without fear that
245
Advanced Programming Topics
GlobalVars.GetTextSize PROCEDURE()
ret SHORT,AUTO
Reader &ISyncObject
CODE
! You need to copy the static variable
! to somewhere safe (A local variable) which
! can then be returned without fear that
! another thread will change it
Reader &= SELF.AccessToGlobals.Reader()
Reader.Wait()
ret = SELF.TextSize
Reader.Release()
RETURN ret
246
Advanced Topics
CriticalProcedure
The CriticalProcedure class is a very easy way to use an ISyncObject interface. If you create a local instance of a
CriticalProcedure and initialize it, then it will look after the waiting for a lock and releasing the lock on the ISyncObject for
you. The main advantage of using the CriticalProcedure class to handle the locking and releasing for you is that if you
have multiple RETURN statements in your procedure, you do not have to worry about releasing the lock before each one.
The destructor of the CriticalProcedure will handle that for you.
For example, the following code
PROGRAM
MAP
WRITETOFILE()
END
INCLUDE('CWSYNCHM.INC')
INCLUDE('CWSYNCHC.INC')
ERRORFILE FILE,DRIVER('ASCII'),PRE(EF)
RECORD RECORD
LINE STRING(100)
END
END
LOCKER &ICRITICALSECTION
CODE
Locker &= NewCriticalSection()
ASSERT(~Locker &= Null)
!do everything
WriteToFile PROCEDURE()
CODE
Locker.Wait()
OPEN(ErrorFile)
IF ERRORCODE()
Locker.Release()
RETURN
END
SET(ErrorFile)
NEXT(ErrorFile)
247
Advanced Programming Topics
IF ERRORCODE()
Locker.Release()
RETURN
END
EF:Line = ‘Something’
PUT(ErrorFile)
CLOSE(ErrorFile)
Locker.Release()
INCLUDE('CWSYNCHM.INC')
INCLUDE('CWSYNCHC.INC')
ERRORFILE FILE,DRIVER('ASCII'),PRE(EF)
RECORD RECORD
LINE STRING(12)
END
END
LOCKER &ICRITICALSECTION
CODE
Locker &= NewCriticalSection()
ASSERT(~Locker &= Null)
!do everything
WriteToFile PROCEDURE()
CP CriticalProcedure
CODE
CP.Init(Locker)
OPEN(ErrorFile)
248
Advanced Topics
IF ERRORCODE()
RETURN
END
SET(ErrorFile)
NEXT(ErrorFile)
IF ERRORCODE()
RETURN
END
EF:Line = ‘Something’
PUT(ErrorFile)
CLOSE(ErrorFile)
A second feature of the CriticalProcedure is that it allows you to RETURN a global (or other non-threaded static) variable
from a prototype that requires protection. The return value will be pushed onto the return stack, and then during cleanup
the Destructor will release the SyncObject. Another other way to RETURN a global string is to copy it to a local variable,
release the SyncObject and RETURN the local string.
Since the base synchronization objects (CriticalSection and Mutex) support nested calls
(wait-wait-release-release), the operating system keeps a wait count, and you must RELEASE as
many times as you WAIT. The CriticalProcedure does not support nested calls (e.g., Init-Init-Kill-Kill). The second call to
CriticalProcedure.Init will release the currently locked object (with a CP.Kill), and then try to lock the newly named object.
There is no check made the exact same object is being "relocked".
CrSec &ICriticalSection
...
CrSec.Wait
....
CrSec.Wait
... release count now 2 ..
CrSec.Release
... release count 1 ..
.. code that needs protection ..
CrSec.Release
249
Advanced Programming Topics
The following section provides a quick summary description of the common properties and methods used with the
Synchronization Objects discussed previously.
Prototypes
Note: The following prototypes can be found in CWSYNCHM.INC
GetMutex
GetSemaphore
NewCriticalSection
NewMutex
NewNamedSemaphore
NewReaderWriterLock
NewSemaphore
GetMutex
GetMutex( name, <error> )
name A string constant or variable that names the IMutex object.
Purpose:
Returns a reference to an IMutex for a Mutex that has already been created with NewMutex( )
If the Mutex has not been previously created, a NULL is returned.
Example:
MySem &IMutex
CODE
MySem &= GetMutex('MyApp_RequestServer')
IF MySem &= NULL
MESSAGE('Server not running')
RETURN(False)
END
250
Advanced Topics
GetSemaphore
Purpose:
Returns a reference to an ISemaphore for a semaphore that has already been created with NewNamedSemaphore( )
If the semaphore has not been previously created, a NULL is returned.
Example:
MySem &ISemaphore
CODE
MySem &= GetSemaphore('MyApp_RequestServer')
IF MySem &= NULL
MESSAGE('Server not running')
RETURN(False)
END
NewCriticalSection
NewCriticalSection ()
Purpose:
Returns a reference to a new ICriticalSection
Example:
! This code is invoked for every started thread before call to ThreadProc.
! Depending on the current thread, Action.Construct executes one of 3
! handler procedures.
Action.Construct PROCEDURE()
CODE
SELF.Threadno = THREAD()
CASE SELF.ThreadNo
OF 1
251
Advanced Programming Topics
NewMutex
NewMutex( ),
NewMutex(name, owner, <error>)
name A string constant or variable that names the new IMutex object.
If a name parameter is not supplied, NewMutex is used for
synchronizing threads only. If a name is supplied, NewMutex
can be used to synchronize multiple processes rather than just
the threads within a process.
Purpose:
Returns a reference to a new IMutex. If the Mutex could not be created, a Null value will be returned. Check the Error
parameter for the reason. (Some reasons for failure can be that another object (e.g., semaphore) exists with the same
name, or a different user has created the object (e.g., security error)).
252
Advanced Topics
NewNamedSemaphore
name A string constant or variable that names the new ISemaphore object.
Purpose:
Returns a reference to a new ISemaphore. This semaphore can be used for synchronizing threads or processes. If an
ERROR_ALREADY_EXISTS (183) is posted to error, then the Semaphore already existed, and initial and max are
ignored.
Using the default settings, NewNamedSemaphore will create a semaphore that no threads can wait on until someone
calls a Release.
NewReaderWriterLock
NewReaderWriterLock ( WritersHavePriority)
Purpose:
Returns a reference to a new IreaderWriterLock
If WritersHavePriority is TRUE, a Writer waiting for ownership of the ReaderWriterLock object will take priority over any
Readers that are waiting for ownership.
If WritersHavePriority is FALSE, all Readers waiting for ownership of the ReaderWriterLock object will take priority over
any Writer waiting for ownership.
253
Advanced Programming Topics
NewSemaphore
Purpose:
Returns a reference to a new ISemaphore
If initial is non-zero, then this indicates how many threads can initially and simultaneously have access to the semaphore
max indicates the maximum number of threads that can simultaneously access the semaphore. By default, this will create
a semaphore that no threads can wait on until a Release is called.
254
Advanced Topics
ISemaphore.Wait( )
Implementation:
This method will wait until shared control of the synchronization object is allowed. Once this occurs, Wait will take partial
control of the synchronization object.
A thread can call Wait( ) multiple times. Release( ) must be called once for each call to Wait.
255
Advanced Programming Topics
ISemaphore.Release( )
Implementation:
Decrements the counter of Wait() calls. When the Wait() count reaches zero, the synchronization object is released for
any other thread to acquire control with a call to Wait().
If more Release() calls are made than Wait() calls, the total number of threads allowed to control the semaphore is
incremented up to the maximum parameter of NewSemaphore.
Count specifies to release a given number of times up to the number of previously successful wait calls.
IReaderWriterLock.Reader( )
Purpose:
Returns a synchronization object that can be used to lock out writers. Any successful call to Reader.Wait() will stop any
attempt at Writer.Wait() until Reader.Release() is called.
IReaderWriterLock.Writer( )
Purpose:
Returns a synchronization object that can be used to lock out any other writer and all readers. Any successful call to
Writer.Wait() will stop any attempt at Writer.Wait() and Reader.Wait() until Writer.Release() is called
CriticalProcedure.Init (syncObj)
Purpose:
Calls syncObj.Wait(). syncObj.Release is called by the destructor.
256
Index
# init_prio 151
#pragma init_priority 144
alias 155 inline 130
ansi 157 inline_max 130
asserts 144 jump 155
BCD_Arithmetic 144 lang_ext 157
BCD_Large 144 line_num 142
BCD_ULONG 144 link 148, 149
big_code 144 linkfirst 148
bit_opr 157 local_implicits 144
c_conv 130 logical_round 144
c_far_ext 138 loop 155
case 149 maincode 144
class_hierarchy 138 map 149
compatible_class 138 min_line 157
compatible_modulus 144 near_call 130
const 155 near_ptr 138
const_assign 138 nest_cmt 157
const_in_code 138 nil_ptr 135
copro 155 o_a_copy 130
cpp_compatible_class 138 o_a_size 130
cpu 155 opt_var_arg 130, 137
cse 155 overflow 135
decode 149 overlay 149
dll_mode 144 pack 149
ds_dynamic 138 packed 138
ds_entry 130 peep_hole 155
ds_eq_ss 130 pre_proc 157
ext_record 138 prefix 153
far_ext 138 proc_trace 142
guard 135 profile 144
heap_size 138 project 159
icon 149 public 142
implementation 151 range 135
incl_cmt 157 reg_param 130
index 135 reg_saved 130
init_code 151 regass 155
257
Advanced Programming Topics
258
Index
259
Advanced Programming Topics
F L
FILE_ALIGNMENT 178 Language Utilities 65
FileCallbackInterface 40 Large Dictionaries 3
FileExists 72 Large WINDOW structures 3
FILETOBLOB 73 Launching a thread - behind the scenes 217
FullDrag 74 LIB_MODE 167
G LIBRARY 178
GetFileDate 75 LINENUMBERS 178
GetFileTime 76 link #pragma 127
GetMutex 249 Link #pragmas 148
GETREG 77 link option #pragma 127
GetSemaphore 249 Link_Option #pragmas 149
GetTempFileName 80 LONGTOHEX 84
GetTempPath 81 M
GetUserName 82 Manifest 178
H Migration issues to Clarion 6 3
Hand coded project files 3 module #pragma 127
Heap Overflow Error 3 Module #pragmas 151
HEAP_COMMIT 178 MSBuild tasks 98
HEAP_RESERVE 178 Multi Language Programming 190
ICriticalSection 235 N
ICriticalSection Methods 254 NAME - EXP files 178
IDE control from command line 105 name #pragma 127
IMAGE_BASE 178 Name #pragmas 153
IMutex 235 New Thread Model 3
IMutex Methods 254 NewCriticalSection 249
INSTANCE - Thread Considerations 226 NewMutex 249
Introduction 108 NewNamedSemaphore 249
IReaderWriterLock 235 NewReaderWriterLock 249
IReaderWriterLock Methods 254 NewSemaphore 249
ISemaphore 235 O
ISemaphore Methods 254 One-piece executables 185
IsTermServer 83 optimize #pragma 127
IWaitable 235 Optimize #pragmas 155
K option #pragma 127
Keycode Equates List 26 Option #pragmas 157
260
Index
261