Async Professional  TM
VCL components for advanced communications
F
For over fifteen years you’ve depended on TurboPower to provide the best
tools and libraries for your development tasks. Now try SysTools 3 and
XMLPartner Professional—two of TurboPower’s best selling products—
The TurboPower family of tools—
Winners of 6 Delphi Informant Readers’ Choice Awards
for 2001! Company of the Year in 2000 and 2001.
risk free. Both are fully compatible with Borland Delphi and C++Builder,

and are backed with our expert support and 60-day money back guarantee.
SYSTOOLS 3
™
D E V E LO P, D E B U G , O P T I M I Z E
time-tested routines you’ll use in virtually every
project you build. For everything from low-level
system access to high-level financial calculations,
SysTools is a product that will easily pay for itself
TM
H E L P S YO U B U I L D YO U R B E S T
library with more than 1000 reliable, optimized,

F R O M S TA RT TO F I N I S H , T U R B O P O W E R
old routines again. That’s because it’s the only
Async
Professional
SysTools 3 means never having to write the same
Async Professional
TM
the first time you use it.
™
TurboPower’s newest cross-platform toolkit for XML (supporting Linux as
well as Windows) provides all the reading, writing, and editing power of
our entry-level product, XMLPartner, then adds advanced manipulation,
transformation, and presentation components—all in a single package!
Try the full range of
TurboPower products.
Download free Trial-Run Editions
from our Web site.
Our new XSL Processor with XPath support, filter set, and EXMLPro utility
make it easy to add XML capabilities to your applications.
www.turbopower.com
Async Professional 4 requires Microsoft Windows (9x,Me,NT,2000 or XP) and Borland Delphi 3 and above,or C++Builder 3 and above
©2001,TurboPower Software Company
Reference Guide
XMLPARTNER PROFESSIONAL
REFERENCE GUIDE
The comprehensive guide to using Async Professional
Async Professional
4
Reference Guide
TurboPower Software Company
Colorado Springs, CO
www.turbopower.com
© 1998-2001 TurboPower Software Company. All rights reserved.
First Edition January 1998
Second Edition November 1999
Third Edition September 2001
™
License Agreement
This software and accompanying documentation are protected by United States copyright law and also by International
Treaty provisions. Any use of this software in violation of copyright law or the terms of this agreement will be prosecuted to
the best of our ability.
Copyright © 1998-2001 by TurboPower Software Company, all rights reserved.
TurboPower Software Company authorizes you to make archival copies of this software for the sole purpose of back-up and
protecting your investment from loss. Under no circumstances may you copy this software or documentation for the
purposes of distribution to others. Under no conditions may you remove the copyright notices made part of the software or
documentation.
You may distribute, without runtime fees or further licenses, your own compiled programs based on any of the source code
of Async Professional. You may not distribute any of the Async Professional source code, compiled units, or compiled
example programs without written permission from TurboPower Software Company.
Note that the previous restrictions do not prohibit you from distributing your own source code, units, or components that
depend upon Async Professional. However, others who receive your source code, units, or components need to purchase
their own copies of Async Professional in order to compile the source code or to write programs that use your units or
components.
The supplied software may be used by one person on as many computer systems as that person uses. Group programming
projects making use of this software must purchase a copy of the software and documentation for each member of the
group. Contact TurboPower Software Company for volume discounts and site licensing agreements.
This software and accompanying documentation is deemed to be “commercial software” and “commercial computer
software documentation,” respectively, pursuant to DFAR Section 227.7202 and FAR 12.212, as applicable. Any use,
modification, reproduction, release, performance, display or disclosure of the Software by the US Government or any of its
agencies shall be governed solely by the terms of this agreement and shall be prohibited except to the extent expressly
permitted by the terms of this agreement. TurboPower Software Company, 15 North Nevada Avenue, Colorado Springs, CO
80903-1708.
With respect to the physical media and documentation provided with Async Professional, TurboPower Software Company
warrants the same to be free of defects in materials and workmanship for a period of 60 days from the date of receipt. If you
notify us of such a defect within the warranty period, TurboPower Software Company will replace the defective media or
documentation at no cost to you.
TurboPower Software Company warrants that the software will function as described in this documentation for a period of
60 days from receipt. If you encounter a bug or deficiency, we will require a problem report detailed enough to allow us to
find and fix the problem. If you properly notify us of such a software problem within the warranty period, TurboPower
Software Company will update the defective software at no cost to you.
TurboPower Software Company further warrants that the purchaser will remain fully satisfied with the product for a period
of 60 days from receipt. If you are dissatisfied for any reason, and TurboPower Software Company cannot correct the
problem, contact the party from whom the software was purchased for a return authorization. If you purchased the product
directly from TurboPower Software Company, we will refund the full purchase price of the software (not including shipping
costs) upon receipt of the original program media and documentation in undamaged condition. TurboPower Software
Company honors returns from authorized dealers, but cannot offer refunds directly to anyone who did not purchase a
product directly from us.
TURBOPOWER SOFTWARE COMPANY DOES NOT ASSUME ANY LIABILITY FOR THE USE OF ASYNC
PROFESSIONAL BEYOND THE ORIGINAL PURCHASE PRICE OF THE SOFTWARE. IN NO EVENT WILL
TURBOPOWER SOFTWARE COMPANY BE LIABLE TO YOU FOR ADDITIONAL DAMAGES, INCLUDING ANY
LOST PROFITS, LOST SAVINGS, OR OTHER INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF
THE USE OF OR INABILITY TO USE THESE PROGRAMS, EVEN IF TURBOPOWER SOFTWARE COMPANY HAS
BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
By using this software, you agree to the terms of this section and to any additional licensing terms contained in the
DEPLOY.HLP file. If you do not agree, you should immediately return the entire Async Professional package for a refund.
All TurboPower product names are trademarks or registered trademarks of TurboPower Software Company. Other brand
and product names are trademarks or registered trademarks of their respective holders.
Table of Contents
1
Chapter 1: Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1
2
Files Supplied . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .4
The Component Hierarchy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .8
Organization of this Manual . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .17
Technical Support . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .19
3
4
Chapter 2: Port Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .21
TApdComPort Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .22
5
Chapter 3: Winsock Components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .99
TApdSocksServerInfo Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
TApdWinsockPort Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
TApdSocket Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
6
7
Chapter 4: Data Packet Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
TApdDataPacket Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
8
Chapter 5: Script Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
TApdScript Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
Chapter 6: State Machine Components . . . . . . . . . . . . . . . . . . . . . . . . . . 165
TApdStateMachine Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172
TApdState Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178
Chapter 7: Status Light Components . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187
TApdStatusLight Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188
TApdSLController Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191
Chapter 8: The Terminal Components . . . . . . . . . . . . . . . . . . . . . . . . . . 195
Terminal Design Considerations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
TAdTerminalBuffer Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
The Terminal Parsers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
TAdTerminalParser Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
TAdVT100Parser Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
The TAdKeyboardMapping Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
The TAdCharSetMapping Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
The TAdTerminalEmulator Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
The TAdTTYEmulator Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
The TAdVT100Emulator Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
196
201
224
229
234
238
245
253
262
263
9
10
11
12
13
14
15
16
17
1
1
1
The TAdTerminal Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .269
Chapter 9: IP Telephony . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293
2
3
4
5
6
7
8
9
IP Telephony in Async Professional . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .294
Configuration for VoIP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .295
TApdVoIPTerminal Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .298
TApdVoIP Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .301
Chapter 10: SAPI Components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 309
SAPI Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .310
TApdAudioOutDevice Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .314
TApdAudioInDevice Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .324
TApdCustomSapiEngine Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .333
Speech Synthesis Tags . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .334
TApdSapiEngine Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .337
TApdSapiPhonePrompts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .351
TApdCustomSapiPhone Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .356
TApdSapiPhone Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .359
Chapter 11: Remote Access Service (RAS) Components . . . . . . . . . . . 371
TApdRasDialer Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .372
TApdRasStatus Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .387
Chapter 12: TAPI Components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 389
10
11
12
13
14
TAPI Device Control from an Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .392
TApdTapiDevice Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .407
TApdAbstractTapiStatus Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .440
TApdTapiStatus Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .443
TApdTapiLog Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .445
Chapter 13: Modem Components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 447
modemcap and libmodem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .448
TApdLibModem Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .449
TAdModem Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .461
TAdModemStatus Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .478
Chapter 14: File Transfer Protocols . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 483
15
16
17
1
1
General Issues . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .485
Xmodem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .501
Ymodem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .504
Zmodem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .507
Kermit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .513
ASCII . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .519
FTP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
TApdProtocol Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
TApdFtpClient Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
TApdAbstractStatus Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
TApdProtocolStatus Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
TApdProtocolLog Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
521
523
559
578
582
583
1
2
3
Chapter 15: Fax Components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 587
Faxmodem Control from an Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Document Conversion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
TApdFaxConverter Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
TApdFaxUnpacker Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
TApdFaxViewer Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
TApdFaxPrinter Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
TApdAbstractFaxPrinterStatus Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
TApdFaxPrinterStatus Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
TApdFaxPrinterLog Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Sending and Receiving Faxes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
TAPI/Fax Integration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
TApdAbstractFax Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
TApdSendFax Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
TApdReceiveFax Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Fax Server Components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
TApdFaxJobHandler Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
TApdFaxServer Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
TApdFaxServerManager Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
TApdFaxClient Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
TApdAbstractFaxStatus Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
TApdFaxStatus Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
TApdFaxLog Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Fax Printer Drivers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
TApdFaxDriverInterface Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
588
591
594
624
649
674
689
693
695
698
704
715
736
754
764
769
779
809
815
822
826
828
830
834
4
5
6
7
8
9
10
11
12
13
Chapter 16: Paging Components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 837
Sending Alphanumeric Pages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
TApdAbstractPager Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
TApdTAPPager Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
TApdSNPPPager Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
TApdPagerLog Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
TApdGSMPhone Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
TApdSMSMessage Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
TApdMessageStore Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
838
839
841
856
862
865
872
875
14
15
16
17
1
1
1
TApdGSMPhone Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .878
Chapter 17: Low-level Facilities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 887
2
Timers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .888
Name Routines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .894
3
Chapter 18: Appendices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 899
4
Error Handling and Exception Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .900
Conditional Defines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .920
Glossary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .921
Debugging Windows Communications Programs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .930
5
Identifier Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . i
6
Subject Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xvii
7
8
9
10
11
12
13
14
15
16
17
1
1
Chapter 1: Introduction
1
2
Async Professional is a collection of native Visual Component Library (VCL) components
that provide serial communication facilities for programs created with Borland Delphi and
C++Builder. It provides optimized components that are fully integrated with Delphi,
compile directly into your EXE files, and include complete source code. Async Professional
(APRO) provides a wide range of communication components, including:
3
4
• A communications port component with standard serial port properties (port
number, baud rate, and so on), methods for sending and receiving data, and events
for common communications situations (data available, buffer empty, and so on).
5
• A flexible data packet component that informs you when data that meets your criteria
6
arrives at the communications port.
• New state machine components that let you design and implement protocols.
7
• New SAPI components to add Speech to your applications. Now your applications
can speak (Text to Speech) and listen (Speech to Text).
8
• New IP Telephony components to implement full streaming audio and video over
your network.
9
• New Non-TAPI modem database using TurboPower’s NEW modemcap XML format.
10
Use the TAPI modem definitions (from the INF files) to control your modem when
TAPI doesn’t cut it.
• New SMS pager component to take advantage of the Short Message System.
11
• A scripting component that contains properties and methods for automating basic
12
communication operations like logging on and off, file upload and file download.
• A communications port component that provides network and Internet
13
communications using Winsock, in addition to the standard communications port
capabilities.
14
• A RAS dialing component to that gives you more control over your Dial-Up
Networking via the Remote Access Server API.
15
• File Transfer Protocol (FTP) components that take care of the FTP protocol details
and present a friendly interface, allowing you to transfer huge files from the Internet
and support resumable transfers. An FTP logging component automates the process
of logging an FTP client-server dialog for auditing FTP activities.
16
17
1
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
• TAPI components for working with modems in TAPI environments like Windows
95/98, Windows NT 4.0, and Windows 2000.
• A new modem component that provides a simple interface for accessing the most
commonly used modem operations. TAdModem integrates the selection of the
modem from the new modem database and the dialog to show the current status of
the modem.
• An advanced terminal the provides full support for VT100 protocol.
• StatusLight components that react to changes in serial port status and reflect the
status of the port.
• A file transfer protocol component for transferring files using an Xmodem, Ymodem,
Zmodem, Kermit, or ASCII protocol.
• File transfer status and file transfer logging components to display the progress of a
file transfer and create a history file of files sent or received.
• Paging components for sending alphanumeric pages with Telelocator Alphanumeric
Protocol (TAP), Personal Entry Terminal Protocol (PET), internet based paging using
the Simple Network Paging Protocol (SNPP), and Short Message Services (SMS).
• Fax Client and Server components that make it easy to create a distributed fax server
system.
• A fax conversion component that converts color BMP, monochrome PCX, DCX,
TIFF and text files to a faxable format, and a fax unpacking component that unpacks
received faxes into image files or memory bitmaps. Components for printing and
viewing faxes are also included.
• Fax printer drivers and an interface component that provide a print-to-fax feature
from any Windows program.
• Fax send and receive components for transmitting and receiving fax files using Class
1, Class 1.0, Class 2 and Class 2.0 faxmodems.
14
15
16
17
2 Chapter 1: Introduction
1
1
Deprecated components
1
As Async Professional has matured through the years, several components have become
obsolete, or have been replaced by components with greater functionality. Some of these
components have been deprecated to allow APRO to evolve, while still maintaining some
degree of backwards compatibility.
2
Previous version of APRO have moved the deprecated components to a separate tab on the
component palette, this version of APRO has deprecated even that. The deprecated
components are installed on your installation destination folder in the \Bonus folder. The
units in this folder contain the component source for several components that can be
installed in your palette.
4
We do not plan to make any enhancements to these components, and technical support for
these products will have a very low priority. These components may be completely removed
from future versions of APRO. In short, we highly recommend that you do not use these
components for new development.
3
6
7
The following components are now deprecated:
• TApdIniDBase, TApdModemDBase, AwModem.ini: These components and files
were used for modem configuration and phone book databases. They have been
replaced by the TApdLibModem component and the modemcap database.
• TApdModem, TApdSModem: These files were used for non-TAPI modem control
using the TApdModemDBase component. They have been replaced by the
TAdModem and TApdLibModem components.
• Modem dialer and status components using the TApdModem and TApdSModem
components.
• Phonebook and phonebook editor components.
• Terminal window, terminal emulator and keyboard emulator components that allow
you to add ANSI, VT52 or VT100 terminals to your application. Replaced by the
TAdTerminal and associated components.
Each of the units containing installable deprecated components are duplications of the
distributed 3.0x source files. To install these components, you may have to add the
registrations methods, or add the units to a custom package.
Documentation for the deprecated components is included in the APRODEP.HLP file,
installed in the \Bonus folder.
8
9
10
11
12
13
14
15
16
17
Chapter 1: Introduction 3
1
1
1
Files Supplied
2
Installation information is provided in the Async Professional Developer’s Guide.
3
Async Professional includes Delphi components, demonstration programs, example
programs, and a help system. It also includes a few general files, which are described below.
README.HLP
4
A help file that describes changes to the manual and new features added after the manual
was printed. Please read this file before using the product.
5
APRO.XXX
6
A text file that summarizes changes between successive versions of Async Professional.
“XXX” is replaced with the version number. For example, APRO.4.01summarizes changes
between versions 4.00 and 4.01 of Async Professional.
7
APRO.HLP or APROBCB.HLP
8
A Windows help file containing information about Async Professional. The help system is
generated from this manual and contains the complete text of the reference sections along
with abbreviated versions of each component introduction.
9
Units supplied
10
11
12
13
14
15
16
The Async Professional components depend on several low-level units that are not
documented in this manual and should never need to be used directly by your program.
These include AdFaxCtl, AdMeter, AdPackEd, AdPropEd, AdRasUtl, AdSelCom, AdTSel,
AdTUtil, and AdWUtil.
The AdXDial, AdXDown, AdXPort, AdXProt, and AdXUp bonus units provide example
dialogs for dialing, downloading, selecting port options, selecting protocol options, and
uploading, respectively. Although these units are not documented in this manual, you can
easily use the forms in your program or modify them for your needs. Units that will be used
in your applications to access the Async Professional VCL components are shown in Table
1.1.
Table 1.1: Async Professional units
Unit
Description
AdAbout
Includes the Version property.
AdExcept
Defines all of the exception classes used by Async
Professional.
17
4 Chapter 1: Introduction
1
1
Table 1.1: Async Professional units (continued)
1
AdFax
Includes the TApdAbstractFax, TApdSendFax, TApdReceiveFax,
TApdFaxLog, and TApdAbstractFaxStatus components.
AdFaxCvt
Includes the TApdFaxConverter and TApdFaxUnpacker components.
AdFaxPrn
Includes the TApdFaxPrinter, TApdFaxPrinterLog, and
TApdAbstractFaxPrinterStatus components.
AdFaxSrv
Includes the TApdFaxJobHandler, TApdFaxServer,
TApdFaxServerManager and TApdFaxClient components.
AdFPStat
Includes the TApdFaxPrinterStatus component.
2
3
4
AdFStat
Includes the TApdFaxStatus component.
AdFtp
Includes the TApdFtpClient and TApdFtpServer components.
AdFView
Includes the TApdFaxViewer component.
AdLibMdm
Includes the TApdLibModem modemcap interface component.
AdModem
Includes the TAdModem and TAdAbstractModemStatus components.
AdPacket
Includes the TApdDataPacket component.
6
7
AdPager
Includes the TApdTAPPager and TApdSNPPPager components.
AdPort
Includes the TApdComPort component.
AdProtcl
Includes the TApdProtocol, TApdProtocolLog, and
TApdAbstractStatus components.
AdPStat
Includes the TApdProtocolStatus component.
AdRas
Includes the TApdRasDialer component.
AdRStat
Includes the TApdRasStatus component.
AdSapiEn
Includes the TApdSapiEngine and TApdSapiPhone components.
AdScript
Includes the TApdScript component.
AdSocket
Includes the TApdSocket component.
AdState
Includes the TApdStateMachne, TApdState and TApdStateWatcher
components.
12
AdStatLt
Includes the TApdStatusLight and TApdSLController components.
13
AdTapi
Includes the TApdTapi, TApdTapiLog, and
TApdAbstractTapiStatus components.
AdTrmBuf
Includes the TAdTerminalBuffer component.
AdTrmEmu
Includes the TAdTerminalEmulator, TAdTTYEmulator,
TAdVT100Emulator and the TAdTerminal components.
AdTrmMap
Includes the TAdKeyboardMapping and TAdCharSetMapping
components.
AdTrmPsr
Includes the TAdTerminalParser and TAdVT100Parser components.
8
9
10
11
14
15
16
17
Files Supplied 5
1
1
1
2
3
4
5
6
7
8
9
Table 1.1: Async Professional units (continued)
AdTStat
Includes the TApdTapiStatus component.
AdVoIP
Includes the TApdVoIP component and associated classes.
AdWnPort
Includes the TApdWinsockPort component.
AproReg is the unit used to register all of the Async Professional components and to add
them to the component palette. Refer to the installation section of the Developer’s Guide for
more information.
Demonstration and example programs
Async Professional includes many demonstration programs and small example programs.
The demonstration programs are intended to be useful as well as instructive and they
include many user-interface niceties that sometimes obscure the use of the communications
objects, while the example programs typically use minimal code allowing you to focus on
the specific component or technique being demonstrated. Generally, you should use the
example programs to understand the basics of the communications components and use the
demonstration programs for real-life examples of implementation.
Table 1.2 briefly describes the demonstration programs. These programs are described fully
in the Developer’s Guide.
Table 1.2: Async Professional demonstration programs
10
Name
Description
TermDemo
Demonstrates the terminal component and related components.
It provides more features than the introductory example
programs but isn’t as complex as TCom3.
11
12
SendFax
Shows how to send multiple faxes with optional cover pages.
RcvFax
Shows how to wait for and answer incoming fax calls.
Cvt2Fax
Demonstrates how the fax converter component converts text,
BMP, PCX, DCX, TIFF files, and the new shell and COM methods.
13
14
ViewFax
A fax viewer that allows you to view APF files.
FaxMon
Monitors the Async Professional fax printer driver for print
jobs and notifies FaxServr. Designed to run with FaxServr.
FaxServr
Retrieves the recipient’s fax number from FaxMon, then sends
the fax. Designed to run with FaxMon.
FaxSrvX
Monitors print jobs sent to the Async Professional fax
printer driver and send the faxes. FaxSrvX is based on
SendFax.
15
16
17
6 Chapter 1: Introduction
1
1
Table 1.2: Async Professional demonstration programs (continued)
1
VoIPDemo
A simple Voice of IP (IP Telephony) program that demonstrates
how to establish VoIP connections and conduct conversations.
TTSDemo
A simple SAPI Text-to-Speech demonstration program.
SRDemo
A simple SAPI Speech Recognition demonstration program.
StatDemo
A project demonstrating the protocol wizard components to
create an automated logon.
ModemCap
A somewhat detailed example demonstrating the modemcap modem
database.
InfParsr
A demonstration program that parses modem INF files and
converts them to modemcap entries.
RasDemo
A simple RAS dialer program that can dial and manipulate RAS
phonebooks. It is based on the TApdRasDialer component.
6
FTPDemo
A simple FTP client program that can connect to an FTP
server, login, transfer files, display directory contents,
etc. It is based on the TApdFtpClient component.
7
ExPaging
2
A simple paging program that allows the user to maintain a
list of pager IDs and access addresses (TAP Paging Server
phone numbers and/or SNPP IP addresses), and to send
alphanumeric pages to them individually or in groups.
The example programs are discussed fully in the “Example” section for each of the
components throughout the manual. See EXAMPLES.TXT in the installation directory for a
list of the example programs included in Async Professional. The list provides the name of
the project (DPR) file for each example. The main form/unit file name typically consists of
the project name followed by ‘0’.
3
4
8
9
10
11
12
13
14
15
16
17
Files Supplied 7
1
1
1
The Component Hierarchy
2
In order to provide the user easy access to a product version number, a Version property is
associated with the Async Professional’s non-visual TApdBaseXxx components. The VCL
ancestor is listed in the hierarchy for each TApdBaseXxx component.
3
Version
4
5
6
7
8
9
10
property Version : string
! Show the current version of Async Professional.
Version is provided so you can identify your Async Professional version if you need
technical support. You can display the Async Professional about box by double-clicking this
property or selecting the dialog button to the right of the property value.
On the following pages are diagrams showing the Async Professional component hierarchy.
All of the diagrams show the component or class name and the unit name where the
component is implemented. All of the Async Professional components derive ultimately
from the TComponent class.
Some of the classes, such as TApdCustomComPort, include the word “Custom” in their
names. These classes follow the Borland convention of implementing all of the properties
and events of a component, but publishing none of them. You don’t design with these
classes, but they are useful for deriving your own components that differ in some way from
the supplied components. They are not described in the following overview.
11
12
13
14
15
16
17
8 Chapter 1: Introduction
1
1
read-only property
ComPort, Winsock, FTP, Data Packet, Socket
1
TComponent (VCL)
2
TApdBaseComponent (OOMisc)
TApdCustomComPort (AdPort)
3
TApdComPort (AdPort)
TApdCustomWinsockPort (AdWnPort)
4
TApdWinsock Port (AdWnPort)
TApdCustomFtpClient (AdFtp)
TApdFtpClient (AdFtp)
6
TApdFtpLog (AdFtp)
7
TApdDataPacket (AdPacket)
TApdSocket (AdSocket)
8
The TApdComPort component is the foundation of Async Professional. It allows you to
access your PC’s serial ports, to set their properties, and to do low level serial
communications. Almost all of the other components in Async Professional contain a link
to a TApdComPort. The TApdWinsockPort provides a Winsock port for use in network and
Internet communications. It provides all of the properties and methods of a TApdComPort,
so can perform either as a Winsock port or a standard communications port. The
TApdFtpClient is a specialized TApdWinsockPort that implements client-side file transfer
protocol (FTP) capabilities. TApdFtpLog can be associated with a TApdFtpClient to provide
automatic FTP logging services. TApdDataPacket provides data packets for incoming data.
TApdSocket is a low-level component that provides standard Winsock services.
Scripting component
9
10
11
12
13
TComponent (VCL)
14
TApdBaseComponent (OOMisc)
TApdCustomScript (AdScript)
15
TApdScript (AdScript)
This diagram shows the hierarchy of the TApdScript component which contains properties
and methods for automating basic communications sessions.
16
17
The Component Hierarchy 9
1
1
1
RAS dialing
TComponent (VCL)
2
3
4
TApdBaseComponent (OOMisc)
TApdCustomRasDialer (AdRas)
TApdRasDialer (AdRas)
TApdCustomRasStatus (AdRas)
TApdRasStatus (AdRStat)
5
6
7
8
9
This diagram shows the components used to establish and monitor a connection to another
computer via Dialup Networking. TApdRasDialer provides an easy to use interface to the
Microsoft Remote Access Services API and a set of standard functions for manipulating
phonebook entries and displaying dial status information. The TApdRasStatus component
provides a standard RAS dialing status dialog for use on machines whose RAS DLL does not
provide a status display.
TAPI modem management
TComponent (VCL)
TApdBaseComponent (OOMisc)
10
11
12
TApdCustomTapiDevice (AdTapi)
TApdTapiDevice (AdTapi)
TApdAbstractTapiStatus (AdTapi)
TApdTapi Status (AdTapi)
TApdTapiLog (AdTapi)
13
14
15
16
This diagram shows the family of components used for Telephony Application
Programming Interface (TAPI) modem management. TApdTapiDevice provides modem
dialing, answering, and configuration services using Windows TAPI support. The
TApdTapiDevice also provides support for advanced voice modem features like WAV file
playing and recording, and DTMF tone detection and generation. TApdAbstractTapiStatus
is an abstract class that can be attached to a TApdTapiDevice object to display the TAPI
status. TApdTapiStatus is an implementation of this abstract class that displays status in a
particular format. TApdTapiLog is a small component that can be attached to a
TApdTapiDevice object to keep a log file of TAPI actions.
17
10 Chapter 1: Introduction
1
1
Modem operations
1
TComponent (VCL)
2
TApdBaseComponent (OOMisc)
TApdCustomSModem (AdSModem)
3
TApdSModem (AdSModem)
This diagram shows the ancestry of the TApdSModem (simple modem) component. It
provides a simple interface for accessing the most commonly used modem operations. It
integrates the selection of the modem from the modem database and the dialog to show the
current status of the modem.
4
Terminal
6
TObject (VCL)
7
TAdTerminalBuffer (ADTrmBuf)
8
TAdKeyboardMapping (ADTrmMap)
TAdCharSetMapping (ADTrmMap)
9
TAdTerminalParser (ADTrmPsr)
TAdVT100Parser (ADTrmPsr)
10
TComponent (VCL)
11
TApdBaseComponent (OOMisc)
TAdTerminalEmulator (ADTrmEmu)
12
TAdTTYEmulator (ADTrmEmu)
13
TAdVT100Emulator (ADTrmEmu)
TWinControl (VCL)
14
TApdBaseWinControl (OOMisc)
TAdTerminal (ADTrmEmu)
15
16
17
The Component Hierarchy 11
1
1
1
2
3
4
5
6
7
8
9
This diagram shows the family of components and classes used for terminals and emulators.
The TAdTerminalBuffer class defines a data structure for maintaining the data required for a
communications terminal display. The TAdTerminalParser class is the ancestor class that
defines the functionality of a terminal parser. The TAdVT100Parser class defines a parser
that understands VT100 terminal data streams.
The TAdKeyboardMapping class provides a simple, convenient method to specify the PC
keystrokes that map onto the emulated terminal keystrokes, and also what control sequence
those terminal keystrokes are going to send to the host computer. The TAdCharSetMapping
class provides a method to emulate the different character sets used by terminals by using
glyphs from different fonts.
The TAdTerminalEmulator class is the base class for all terminal emulators. The
TAdTTYEmulator class emulates a teletype terminal. The TAdVT100Emulator class
emulates a Digital Equipment Corporation (DEC) VT100 terminal and supports some
common extensions to the normal VT100 escape sequence set.
The TAdTerminal component represents the visual part of a terminal. It is the only visual
component in the Async Professional suite of terminal classes. It is responsible for
maintaining the window handle and for performing the low-level processing to get all the
possible keystrokes available on a PC keyboard.
Modem status lights
TComponent (VCL)
10
11
12
TApdBaseComponent (OOMisc)
TApdCustomSLController (AdStatLt)
TApdSLController (AdStatLt)
TControl (VCL)
TGraphicControl (VCL)
13
TApdCustomStatusLight (AdStatLt)
TApdStatusLight (AdStatLt)
14
15
16
This diagram shows the family of components used for modem status lights.
TApdStatusLight is a graphical component used to display the light in a “lit” or “unlit” state.
TApdSLController manages a group of TApdStatusLight components and lights them based
on events it receives from an associated comport component.
17
12 Chapter 1: Introduction
1
1
Paging
1
TComponent (VCL)
2
TApdBaseComponent (OOMisc)
TApdAbstractPager (AdPager)
3
TApdCustomModemPager (AdPager)
TApdTAPPager (AdPager)
4
TApdCustomINetPager (AdPager)
TApdSNPPPager (AdPager)
This diagram shows the family of components used for sending alphanumeric pages.
TApdAbstractPager provides properties and methods that are common to paging regardless
of the transmission medium. TApdTAPPager sends alphanumeric pages to paging services
that support Telelocator Alphanumeric Protocol and the Motorola Personal Entry Terminal
Protocol. TApdSNPPPager implements Internet based paging using the Simple Network
Paging Protocol.
6
7
8
9
10
11
12
13
14
15
16
17
The Component Hierarchy 13
1
1
1
Faxes: conversion, unpacking, viewing, printing
TComponent (VCL)
2
3
4
!TApdBaseComponent (OOMisc)
!TApdCustomFaxConverter (AdFaxCvt)
!TApdFaxConverter (AdFaxCvt)
!TApdCustomFaxUnpacker (AdFaxCvt)
!TApdFaxUnpacker (AdFaxCvt)
5
6
!TApdCustomFaxPrinter (AdFaxPrn)
!TApdFaxPrinter (AdFaxPrn)
!TApdAbstractFaxPrinterStatus (AdFaxPrn)
7
8
9
!TApdFaxPrinterStatus (AdFaxPrn)
!TApdCustomFaxPrinterLog (AdFaxPrn)
!TApdFaxPrinterLog (AdFaxPrn)
!TControl (VCL)
!TWinControl (VCL)
10
!TApdCustomFaxViewer (AdView)
!TApdFaxViewer (AdView)
11
12
13
14
15
This diagram shows the family of components used for fax conversion, unpacking, viewing,
and printing. TApdFaxConverter converts ASCII text, BMP, PCX, DCX, and TIFF files to
Async Professional Fax (APF) format. TApdFaxUnpacker unpacks fax files to memory
bitmaps or image files. TApdFaxViewer allows viewing of faxes with scaling, rotation, and
white space compression. TApdFaxPrinter prints faxes with scaling and headers and footers.
TApdAbstractFaxPrinterStatus is an abstract class that can be attached to a TApdFaxPrinter
object to display print status. TApdFaxPrinterStatus is an implementation of this abstract
class that displays status in a particular format. TApdFaxPrinterLog is a small component
that can be attached to a TApdPrinter object to keep a log file of printer actions.
16
17
14 Chapter 1: Introduction
1
1
Faxes: sending, receiving
1
TComponent (VCL)
2
!TApdBaseComponent (OOMisc)
!TApdCustomAbstractFax (AdFax)
3
!TApdAbstractFax (AdFax)
!TApdCustomSendFax (AdFax)
4
!TApdSendFax (AdFax)
!TApdCustomReceiveFax (AdFax)
!TApdReceiveFax (AdFax)
6
!TApdCustomFaxServer
7
!TApdFaxServer
!TApdFaxJobHandler
8
!TApdFaxServerManager
!TApdFaxClient
9
!TApdAbstractFaxStatus (AdFax)
10
!TApdFaxStatus (AdFax)
!TApdFaxLog (AdFax)
11
!TApdFaxDriverInterface (AdFaxCtl)
This diagram shows the family of components used for fax sending and receiving.
TApdAbstractFax provides the set of properties, methods, and events that are common to
both sending and receiving. The send and receive components are derived from
TApdAbstractFax. TApdSendFax sends single or multiple faxes with optional cover pages.
TApdReceiveFax receives faxes. TApdFaxServer is the faxing engine for the Fax Server
Components. It handles the physical communication with the fax modem to send and
receive faxes. Since this component can both transmit and receive faxes, it shares many
properties with TApdSendFax and TApdReceiveFax.
TApdFaxJobHandler provides methods to manipulate the Async Professional Job file
format. TApdFaxServerManager component provides fax scheduling and queuing
capability. TApdFaxClient provides the ability to create APJ fax job files with a design-time
interface.
12
13
14
15
16
17
The Component Hierarchy 15
1
1
1
2
3
TApdAbstractFaxStatus is an abstract class that can be attached to a TApdSendFax,
TApdReceiveFax or TApdFaxServer object to display fax status. TApdFaxStatus is an
implementation of this abstract class that displays status in a particular format. TApdFaxLog
is a small component that can be attached to a TApdSendFax, TApdReceiveFax or
TApdFaxServer object to keep a log file of fax actions. TApdFaxDriverInterface provides
control for the fax printer drivers.
4
5
6
7
8
9
10
11
12
13
14
15
16
17
16 Chapter 1: Introduction
1
1
Organization of this Manual
1
This manual is organized as follows:
2
• Chapter 1 is an introduction to Async Professional.
• Chapters 2 through 16 describe the Async Professional components.
3
• Chapter 17 describes a few general non-communication components.
4
• The appendices provide a discussion of error handling, a description of the Async
Professional conditional defines, and a glossary of communications terms.
• An identifier index and a conventional subject index are provided.
Each chapter starts with an overview of the classes and components discussed in that
chapter. The overview also includes a hierarchy for those classes and components. Each class
and component is then documented individually, in the following format:
Overview
6
7
8
A description of the class or component.
9
Hierarchy
Shows the ancestors of the class being described, generally stopping at a VCL class. The
hierarchy also lists the unit in which each class is declared and the number of the first page of
the documentation of each ancestor. Some classes in the hierarchy are identified with a
number in a bullet: !. This indicates that some of the properties, methods, or events listed
for the class being described are inherited from this ancestor and documented in the
ancestor class.
Properties, methods, and events lists
10
11
12
13
The properties, methods, and events for the class or component are listed. Some of these
may be identified with a number in a bullet: !. In these cases, they are documented in the
ancestor class from which they are inherited.
14
15
16
17
Organization of this Manual 17
1
1
1
2
Reference section
Details the properties, methods, and events of the class or component. These descriptions
are in alphabetical order. They have the following format:
• Declaration of the property, method, or event.
3
• Default value for properties, if appropriate.
4
• A short, one-sentence purpose. A !symbol is used to mark the purpose to make it
5
• Description of the property, method, or event. Parameters are also described here.
6
• The “See also” section lists other properties, methods, or events that are pertinent to
easy to skim through these descriptions.
• Examples are provided in many cases.
this item.
7
8
9
10
Throughout the manual, the "symbol is used to mark a warning or caution. Please pay
special attention to these items.
Naming conventions
To avoid class name conflicts with components and classes included with the compiler or
from other third party suppliers, all Abbrevia class names begin with “TAb.” The “Ab”
stands for Abbrevia.
12
“Custom” in a component name means that the component is a basis for descendant
components. Components with “Custom” as part of the class name do not publish any
properties. Instead, descendants publish the properties that are applicable to the derived
component. If you create descendant components, use these custom classes instead of
descending from the component class itself.
13
On-line help
11
14
15
Although this manual provides a complete discussion of Abbrevia, keep in mind that there is
an alternative source of information available. Once properly installed, help is available from
within the IDE. Pressing <F1> with the caret or focus on an Abbrevia property, routine or
component displays the help for that item.
16
17
18 Chapter 1: Introduction
1
1
Technical Support
1
The best way to get an answer to your technical support questions is to post it in the Async
Professional newsgroup on our news server (news.turbopower.com). Many of our customers
find the newsgroups a valuable resource where they can learn from others’ experiences and
share ideas in addition to getting answers to questions
2
To get the most from the newsgroups, we recommend that you use dedicated newsreader
software. You’ll find a link to download a free newsreader program on our web site at
www.turbopower.com/tpslive.
Newsgroups are public, so please do NOT post your product serial number, 16-character
product unlocking code or any other private numbers (such as credit card numbers) in
your messages.
The TurboPower KnowledgeBase is another excellent support option. It has hundreds of
articles about TurboPower products accessible through an easy to use search engine
(www.turbopower.com/search). The KnowledgeBase is open 24 hours a day, 7 days a week. So
you will have another way to find answers to your questions even when we’re not available.
Other support options are described in the product support brochure included with Async
Professional. You can also read about support options at www.turbopower.com/support.
3
4
6
7
8
9
10
11
12
13
14
15
16
17
Technical Support 19
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
20 Chapter 1: Introduction
1
1
Chapter 2: Port Component
1
2
The Async Professional port component builds the foundation for all communications
applications. The simplest application might contain a single TApdComPort; a more
complex application might contain one or more port components and many other
components that rely on the services of a TApdComPort (terminal windows, modems, and
so on).
3
4
This chapter describes the TApdComPort component, which contains properties and
methods for the following:
5
• Configuring the serial port hardware and Windows communications driver (buffer
sizes, line parameters, flow control).
6
• Providing information about the state of the serial port (modem signals, line errors).
• Transmitting and receiving data.
7
• Interfacing with the TApdDataPacket component (see page 132) to identify and
8
handle received data.
• Assigning VCL events to handle received data, matched strings, status changes, and
9
timers.
In addition to its support for standard serial ports, the TApdComPort includes specialized
support for the RS-485 communication standard (see page 31).
10
If you need to support network or Internet communications using Winsock, you should
consider using the TApdWinsockPort (see page 106) instead of the TApdComPort. Since the
TApdWinsockPort is a descendant of the TApdComPort, it retains all capabilities of the
TApdComPort, and adds support for Winsock.
11
12
13
14
15
16
17
21
1
1
1
TApdComPort Component
2
An application uses the TApdComPort component to control serial port hardware. All serial
port I/O is performed by calling methods of TApdComPort and by writing event handlers
that respond to serial events. Higher level communications actions such as dialing a modem
or transferring a file use a TApdComPort to interact with the hardware.
3
4
5
6
7
8
9
10
11
12
13
Sending and receiving data through the serial port is obviously part of the process, but most
communications applications also need to identify and handle data according to a specific
need. Async Professional provides many high level components that simplify common tasks
such as displaying the data (see “Chapter 11: Status Light Components” on page 373) and
transferring data using an error-correcting protocol (see “Chapter 12: File Transfer
Protocols” on page 383).
In addition to such common and well-defined tasks, a flexible component is provided that
allows you to define the type of data you are looking for, automatically collect the data for
you, and notify you when the data has arrived (see “Chapter 3: Data Packet Component” on
page 105). Be sure to investigate whether or not these high level components could meet
your needs before venturing too far into the low level facilities of the TApdComport
discussed in the following section—you could save yourself a lot of time and effort.
Triggers and trigger handlers
Async Professional uses the term “trigger” for any serial port action that can cause its
communications dispatcher to generate a VCL event. There are four types of triggers:
• Data available—received data is available.
• Data match trigger—a particular character or character string was received.
• Status trigger—a status event occurred (details later in this section).
• Timer trigger—a timer expired.
14
Note: In 32-bit applications, TApdComPort events are synchronized to the thread that sets
Open to True (in other words, the thread that instantiates the dispatcher). In most cases, this
will end up being the main VCL thread.
15
The TApdComPort component contains a variety of routines for managing triggers.
Triggers can be added, activated, modified, and deactivated.
16
17
22 Chapter 2: Port Component
1
1
For example, adding a timer trigger looks something like this:
var
TrigHandle : Word;
...
TrigHandle := ApdComPort.AddTimerTrigger;
ApdComPort.SetTimerTrigger(TrigHandle, 36, True);
This code adds a timer trigger and stores the trigger handle in TrigHandle. The timer is
activated and set to expire in 36 ticks (about 2 seconds). When the timer expires, an
OnTriggerTimer event will be generated. An event handler must be assigned to the
OnTriggerTimer property to gain control when the timer expires.
procedure Form1.TriggerTimer(CP : TObject; TriggerHandle : Word);
begin
...
end;
...
ApdComPort.OnTriggerTimer := TriggerTimer;
Here are the TApdComPort event properties and event handler declarations that correspond
to the four Async Professional trigger types, and the data they contain.
1
2
3
4
6
7
8
OnTriggerAvail
procedure(CP : TObject; Count : Word) of object;
9
Generated when a certain number of bytes of received serial data are available for
processing. Count is the actual number of bytes that have been received at the instant the
trigger is generated.
It’s likely that more than one byte of data will be available when the message handler is
called. The amount of data received in a given event depends on several factors, such as the
nature of the data itself (does it get sent one character at a time by the transmitter, or is it
being sent in blocks or streams?), and the hardware/driver supplying the data to Async
Professional (standard Windows communications drivers typically supply data in small
blocks (8 bytes or less), but Winsock can supply data in large blocks (1,000 bytes or more).
" Caution: Be sure to process the exact number of bytes passed in the Count parameter of this
handler. If you process fewer bytes, you risk losing characters to another component
extracting data during the event (such as the terminal). If you process a number of bytes
greater than the value of the Count property, you risk receiving events for overlapping
data—which may eventually lead to an EBufferIsEmpty exception.
10
11
12
13
14
15
16
17
TApdComPort Component 23
1
1
1
2
3
4
5
6
7
8
9
Here’s what a typical OnTriggerAvail event handler should look like:
procedure Form1.TriggerAvail(CP : TObject; Count : Word);
var
I : Word;
C : Char;
begin
for I := 1 to Count do begin
C := ApdComPort.GetChar;
...
end;
end;
The OnTriggerAvail and OnTriggerData events are generated from the dispatcher thread,
and synchronized with the appropriate thread through the use of SendMessageTimeout.
The timeout value is 3 seconds. If your OnTriggerAvail or OnTriggerData event handlers
take longer than 3 seconds to execute (starting at the time the SendMessageTimeout method
was called), you run the risk of losing data. If the timeout period expires, the dispatcher will
assume that a deadlock has occurred and will continuing processing the serial port
notification messages. If you need to perform lengthy processing, you should collect the
received data in a buffer, then process the buffer outside the context of the OnTriggerAvail
event handler.
OnTriggerData
procedure(CP : TObject; TriggerHandle : Word) of object;
10
11
12
13
14
15
16
17
Generated when the dispatcher finds a match in the received data for a data string previously
specified using the AddDataTrigger method. TriggerHandle is the trigger handle returned
by AddDataTrigger.
Usually the dispatcher finds the match in the middle of a block of bytes it is examining. In
this case the dispatcher first generates an OnTriggerAvail event for all the data leading up the
matched string, then it generates the OnTriggerData event to advertise the match, and
finally it generates another OnTriggerAvail event for the data associated with the match.
The data trigger does not guarantee that the notification will be exactly synchronized with
the actual occurrence of the data match. In other words, if you set a data trigger for the
string “MYDATA”, it is not reliable nor was it intended for you to start capturing those
characters in an OnTriggerAvail event immediately following the OnTriggerData event for
that string. Refer to the APROFAQ.HLP file for example code.
" Caution: Be sure to process the exact number of bytes passed in the Count parameter of this
handler. If you process fewer bytes, you risk losing characters to another component
extracting data during the event (such as the terminal).If you process a number of bytes
greater than the value of the Count property, you risk receiving events for overlapping
data—which may eventually lead to an EBufferIsEmpty exception.
24 Chapter 2: Port Component
1
1
In most cases, the TApdDataPacket component is the best way to capture a specific string.
See “Chapter 3: Data Packet Component” on page 105 for more information on how to use
the TApdDataPacket component.
1
OnTriggerTimer
2
procedure(CP : TObject; TriggerHandle : Word) of object;
Generated when the dispatcher determines that a timer has expired. TriggerHandle is the
trigger handle returned when AddTimerTrigger was called.
3
4
OnTriggerStatus
procedure(CP : TObject; TriggerHandle : Word) of object;
Generated when a status change occurs. Status types include: changes in modem signals
(CTS, DSR, RING, and DCD), changes in line status (line break received and data overrun,
parity, and framing errors), output buffer free space reaching a specified level, and output
buffer used space reaching a certain level.
TriggerHandle is the trigger handle returned when AddStatusTrigger was called.
When a status trigger is used to track more than one modem signal, the event handler must
check the appropriate modem status properties to determine exactly which modem signal
generated the trigger. You can avoid these calls by adding a separate trigger for each modem
signal that you need to trap.
Specialized versions of the OnTriggerStatus event are also available through several events
that are generated only when one particular kind of status change occurs. Event handlers for
these events are called after the OnTriggerStatus event handler. It is possible to have an
OnTriggerStatus handler and also one or more specific status event handlers. Here are the
event properties and event handler declarations for these specialized events.
6
7
8
9
10
11
OnTriggerLineError
procedure(
CP : TObject; Error : Word; LineBreak : Boolean) of object;
12
Generated when the dispatcher determines that a line error or break signal occurred while
receiving data. Error contains one of the following values (a subset of all possible values of
the LineError property):
13
leOverrun = 2;
leParity = 3;
leFraming = 4;
14
15
LineBreak is True if a break signal was received. Note that breaks are often accompanied by
framing errors.
16
17
TApdComPort Component 25
1
1
1
2
3
4
OnTriggerModemStatus
procedure(CP : TObject) of object;
Generated when a monitored modem status signal changes. The signals to monitor are
passed in SetStatusTrigger. Note that there is no TriggerHandle parameter to this event
handler. As such, it cannot differentiate among multiple modem status triggers. If you need
to add more than one modem status trigger, use an OnTriggerStatus event handler instead
of this specialized one.
OnTriggerOutbuffFree
procedure(CP : TObject) of object;
5
Generated when the number of bytes free in APro’s output buffer is greater than or equal to
the number passed to SetStatusTrigger for this event.
6
OnTriggerOutbuffUsed
procedure(CP : TObject) of object;
7
Generated when the number of bytes used in Apro’s output buffer is less than or equal to the
number passed to SetStatusTrigger for this event.
8
OnTriggerOutSent
procedure(CP : TObject) of object;
9
Generated after PutChar, PutBlock, or PutString is called.
10
The TApdComPort component has one more event type that is a superset of all of those just
described. This event is generated for every trigger reported by the Async Professional
dispatcher to the component.
11
OnTrigger
12
13
14
15
16
procedure(
CP : TObject; Msg, TriggerHandle, Data : Word) of object;
Generated for all trigger events. Msg is the internal message number that corresponds to the
event. For example, the message APW_TRIGGERAVAIL corresponds to the OnTriggerAvail
event (the other message names are documented in the reference section for the OnTrigger
event on page 69). TriggerHandle is the trigger handle returned when the trigger was added.
Data is the data associated with this trigger.
This event is generated prior to the associated specific event type. For example, when a
modem status signal generates a trigger, the OnTrigger event is generated with Msg set to
APW_TRIGGERSTATUS, then the OnTriggerStatus event is generated with TriggerHandle
set to the status trigger handle, then the OnTriggerModemStatus event is generated.
17
26 Chapter 2: Port Component
1
1
In most cases you would not want to use an OnTrigger event handler, but instead provide
individual event handlers. OnTrigger is provided primarily for script or protocol-oriented
processes where it is more efficient to manage all tasks from one central routine.
With the exceptions of OnTriggerAvail, OnTriggerData, and OnTriggerOutSent, all triggers
must be reactivated within the event handler. That is, the triggers generate a single message
and do not restart themselves automatically. The following example uses triggers:
{$IFDEF Win32}
{$APPTYPE CONSOLE}
{$ENDIF}
unit Extrig0;
1
2
3
4
interface
uses
{$IFNDEF Win32}
WinCrt,
{$ENDIF}
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics,
Controls, Forms, Dialogs, StdCtrls, AdPort, AdTerm;
type
TExTrigTest = class(TForm)
ApdComPort1: TApdComPort;
StartTest: TButton;
Label1: TLabel;
procedure ApdComPort1TriggerAvail(
CP : TObject; Count : Word);
procedure ApdComPort1TriggerData(
CP : TObject; TriggerHandle : Word);
procedure ApdComPort1TriggerTimer(
CP : TObject; TriggerHandle : Word);
procedure StartTestClick(Sender : TObject);
private
TimerHandle : Word;
DataHandle : Word;
end;
var
ExTrigTest: TExTrigTest;
implementation
{$R *.DFM}
6
7
8
9
10
11
12
13
14
15
16
17
TApdComPort Component 27
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
procedure WriteIt(C : Char);
begin
if Ord(C) > 32 then
Write(C)
else
Write('[', Ord(C), ']');
end;
procedure TExTrigTest.ApdComPort1TriggerAvail(
CP : TObject; Count : Word);
var
I : Word;
C : Char;
begin
WriteLn('OnTriggerAvail event: ', Count, ' bytes received');
for I := 1 to Count do begin
C := ApdComPort1.GetChar;
WriteIt(C);
end;
WriteLn;
WriteLn('--------');
end;
procedure TExTrigTest.ApdComPort1TriggerData(
CP : TObject; TriggerHandle : Word);
var
I : Word;
C : Char;
begin
WriteLn('OnTriggerData event: ', TriggerHandle);
end;
procedure TExTrigTest.ApdComPort1TriggerTimer(
CP : TObject; TriggerHandle : Word);
begin
WriteLn('OnTriggerTimer event: ', TriggerHandle);
end;
procedure TExTrigTest.StartTestClick(Sender : TObject);
begin
TimerHandle := ApdComPort1.AddTimerTrigger;
ApdComPort1.SetTimerTrigger(TimerHandle, 91, True);
DataHandle := ApdComPort1.AddDataTrigger('OK', True);
16
{send a string to a modem that will hit all triggers}
ApdComPort1.PutString('ATI'^M);
end;
17
end.
28 Chapter 2: Port Component
1
1
This is the unit containing the form for the example project EXTRIG. It contains two
components: StartTest (a TButton) and ApdComPort1 (a TApdComPort). StartButton
implements a Click event handler named StartButtonClick that starts the trigger
demonstration. StartButtonClick adds and sets a timer for 91 ticks (5 seconds) and adds a
data trigger for the string “OK.” Finally, it transmits the string ‘ATI’^M. If a modem is
attached to the serial port, this tells the modem to return its version information, followed
by the response “OK.”
The form implements three event handlers:
1
2
3
4
• ApdComPort1TriggerAvail for OnTriggerAvail events.
• ApdComPort1TriggerData for OnTriggerData events.
• ApdComPort1TriggerTimer for OnTriggerTimer events.
As the modem returns its version information the program receives one or more
OnTriggerAvail events. The handler for this event, ApdComPort1TriggerAvail, retrieves and
displays this data to the WinCrt window (or console window in a 32-bit console
application). ApdComPort1TriggerData is called when “OK” is received; it writes a simple
message to the WinCrt window. After 5 seconds ApdComPort1TriggerTimer is called,
which also writes a short message to the WinCrt window.
ISDN support overview
6
7
8
9
Integrated Service Digital Network (ISDN) connections are becoming more commonplace
in today’s communications applications as the associated costs steadily decline.
ISDN introduces a number of features to the world of PC communications. ISDN lines
have digital channels as opposed to the analog lines (also called POTS for Plain Old
Telephone Service) that standard AT-compatible modems use. Digital channels provide
much higher bandwidths that allow faster communication speeds and higher data
throughput. ISDN lines consist of multiple channels, whereas POTS consists of a single line.
With multiple channels, various types of information (e.g., data, voice, and video) can be
transmitted simultaneously.
ISDN is available in a number of configurations. Basic Rate Interface ISDN (BRI-ISDN)
comes with two B-channels and a D-channel. B-channels allow for 64 Kbps or 56 Kbps
throughput depending on what your local carrier provides. In some configurations, these
channels can be utilized as a single 128 Kbps or 112 Kbps line. The D-channel provides a 16
Kbps channel that is normally used to send signaling information and additional control
data (e.g., the calling party’s name, location, and configuration). Primary Rate Interface
ISDN (PRI-ISDN) is much more expensive and provides up to 23 B-channels and one Dchannel in the U.S., Canada, and Japan. It provides up to 30 B-channels and two D-channels
in Europe.
10
11
12
13
14
15
16
17
TApdComPort Component 29
1
1
3
Async Professional supports basic ISDN services with many AT-compatible adapters
through the TApdTapiDevice component. In general, non-TAPI and non-AT-compatible
ISDN adapters (e.g., those used strictly for PPP or dial-up connections) are not supported
by Async Professional for serial communications. The TApdTapiDevice treats ISDN
adapters as standard AT-compatible modems that control a single channel. A
TApdTapiDevice cannot control multiple channels. You can use the other channels in your
ISDN connection by creating a TApdTapiDevice for each one.
4
Note that Async Professional support ISDN devices through the TApdTapiDevice interface.
Async Professional does not support the CAPI interface to ISDN devices.
1
2
5
6
7
8
9
10
Async Professional supports two types of ISDN adapter/driver configurations: ISDN
adapters that are AT-compatible and ISDN adapters that come with AT-compatible analog
drivers. Many AT-compatible ISDN adapters work with Async Professional transparently,
usually with higher throughput than standard faxmodems. These modems and drivers are
used for ISDN to ISDN connections. ISDN adapters that provide analog modem or fax
drivers work with Async Professional just like a standard faxmodem (i.e., at the lower speed
and throughput of a standard faxmodem). When the driver is the selected device, the ISDN
adapter and line can connect to standard POTS devices such as a faxmodem or fax machine.
To date, there are relatively few ISDN adapters that provide analog fax drivers.
Async Professional can be used with ISDN adapters that conform to the following
specifications:
• 100% AT (command set) compatible.
• TAPI compatible and has a TAPI driver (available in the Modems Applet in the
Microsoft Windows Control Panel).
11
12
13
14
15
16
• At least one data channel.
Async Professional also supports the following optional features:
• Analog modem (TAPI) driver support (used when connecting to standard modems
on POTS lines).
• Faxmodem (TAPI) driver support (used when connecting to standard faxmodems or
fax machines on POTS lines).
Some TAPI service provider drivers do not support the full set of TAPI functions. You might
be limited by the driver provided by your ISDN adapter manufacturer.
Using a TApdTapiDevice component with an ISDN line is exactly the same as using it with a
standard analog line. See “Chapter 8: TAPI Components” on page 203 for more information
on how to use a TApdTapiDevice.
17
30 Chapter 2: Port Component
1
1
RS-485 support overview
1
RS-485 serial networks usually consist of two or more serial devices all connected to the
same 2-wire serial cable. The transmitted data is represented by voltage differences between
the two lines instead of a voltage difference between a single line and a common ground as
in RS-232. This difference allows RS-485 networks to operate over much greater distances
than RS-232.
RS-485 requires specific serial port hardware that supports RS-485 voltages and
conventions. Most standard serial ports provided on a motherboard and even most add-in
serial ports do not support RS-485 mode.
Since both RS-485 wires are required to transmit data, an RS-485 device can either receive
data or transmit data (but not both) at any given moment. RS-485 devices usually spend
most of their time in receive mode, monitoring the line for incoming data. When one device
starts transmitting all other devices in the network receive that data, so messages usually
include an address byte to allow devices to ignore messages not addressed to them.
With such a network, the PC normally acts as a master—addressing and sending data to
each remote slave device and processing its response before moving on to the next device.
Before the PC can transmit, it must take control of the data line. While transmitting, it
cannot receive any data so it must release control of the line after transmitting so it can
receive the response. This switch from transmit to receive mode can be either automatic
(controlled by the RS-485 board or converter), or it can be manual (controlled by the PC
software or driver).
The mechanism provided by RS-485 boards for switching the data line from receive to
transmit mode falls into three categories:
• RTS Control
2
3
4
6
7
8
9
10
11
• Automatic
12
• Other
Most currently available RS-485 boards use the RTS line to control the state of the data line.
Before transmitting data, the application raises the RTS line of the port, which tells the
RS-485 board to switch to transmit mode. After transmitting the data the application lowers
RTS to switch the line back to receive mode. These boards are supported by the
TApdComport by using the RS485Mode property (with some exceptions noted below).
Some boards and converters handle the RS-485 data line switch automatically, with no
assistance from the software. These boards are supported by Async Professional but do not
require use of the RS485Mode property.
13
14
15
16
17
TApdComPort Component 31
1
1
1
2
The few remaining boards use proprietary techniques for providing RS-485 support instead
of the RTS or automatic switching described above. These boards are not specifically
supported by Async Professional, but can probably be used anyway if your code performs
the actions required by the board’s documentation.
RTS control
3
4
5
6
7
8
9
10
Since the TApdComPort provides an RTS property your application could manually raise
RTS before transmitting data and lower RTS after transmitting. This would work in theory,
but is somewhat problematic in that when a PutXxx method has returned, the data may not
have been completely transmitted and lowering RTS at that time would result in some data
being truncated. Even lowering RTS after a calculated delay would be error prone since the
calculation would have to account for delays in UART (the serial port chip) buffering and
would be susceptible to unpredictable delays due to multitasking.
A better approach is to use Async Professional’s built-in RTS line control, which is available
through the RS485Mode property. When RS485Mode is set to True all PutXxx methods
raise RTS before transmitting the first byte, wait for the data to be completely transmitted,
then lower RTS. The wait accurately accounts for data in the UART, assuring that RTS is
lowered at the proper time.
" Caution: The RTS line control follows the output buffer. If the output buffer empties while
your code is formatting and transmitting a command, the RTS line could be lowered and
re-raised—which might cause some RS-485 devices to misinterpret the message.
It is better to pre-format a command in a buffer and use a single PutBlock call to transmit it
than to format and transmit at the same time using multiple PutXxx commands.
For example, use the first code example rather than the second:
11
Message := '!' + Address + MsgLength + Message + '$';
PutString(Message);
12
PutChar('!');
PutChar(Address);
PutChar(MsgLength)
PutString(Message, MsgLength);
PutChar('$');
13
14
15
Under Windows 95/98/ME, the waiting is handled within Async Professional since the
communications API doesn’t provide the necessary accuracy. Under Windows NT, the
waiting is handled by the serial port driver, since it does provide the necessary accuracy.
16
17
32 Chapter 2: Port Component
1
1
The automatic handling of the RTS line is possible for standard ports in all environments
and for all non-standard serial ports that provide the necessary driver support. Part of this
support (in Windows 95/98/ME) includes the detection of the serial port hardware’s base
address. If this address cannot be detected, attempts to transmit in RS-485 mode will raise
the EBaseAddressNotSet exception. If you know the base address of the port, you can set it
manually using the BaseAddress run-time property.
If the serial port doesn’t use standard serial port hardware, then RS485Mode cannot provide
automatic RTS line control for that port. In such cases, however, the board likely provides
some other mechanism for handling RS-485 support (assuming it’s RS-485 capable) and
you will need to consult the board’s documentation for details.
Under Windows NT, the waiting is handled within the serial port driver and replacement
drivers must also provide this support in order for Async Professional’s RS485Mode
property to work. If they do not, it is again likely that the board provides some other
mechanism for supporting RS-485.
Debugging facilities
1
2
3
4
6
7
In a perfect world all programs would work flawlessly as they were typed in. Since things
rarely work out this nicely, it is often necessary to break out the debugging tools and apply
some hard-won debugging knowledge to get programs to behave themselves.
Communications programs introduce some new debugging issues, and your existing tools
and knowledge may no longer be adequate.
8
9
For example, suppose you’re writing a data collection program that regularly receives data
from an instrument and writes the data to a database. While testing, you notice that a small
percentage of the data in the database is wrong. Broadly speaking, there are two
explanations for such a problem: 1) the instrument sent bad data; or 2) your program
somehow corrupted the good data before writing it to disk. Given that the errors occur
infrequently, you’d probably have to add specific debugging code to your program to create
an audit report of all received data. Later you’d compare this audit report to the data in the
database. If the data matched, you would know that the instrument sent bad data; otherwise
you could conclude that your program corrupted the data. Either way, you would know what
debugging steps to take next.
10
Tracing
14
Variations of this need for an audit trail can occur in almost any communications program.
Rather than force you to add such debugging code to your applications, Async Professional
provides a general auditing facility called tracing.
15
Simply put, tracing gives you the ability to keep track of all characters transmitted and
received by a program, in the form of a text file that your program creates on request.
11
12
13
16
17
TApdComPort Component 33
1
1
1
2
3
4
5
6
Every time an application successfully retrieves a received character (i.e., GetChar returns a
character) a trace entry is created. Every time an application successfully sends a character
to the Windows communications driver (i.e., PutChar is called successfully) another trace
entry is created.
These entries are stored in a circular queue of a specified size. Since the queue is circular, it
always contains information about the most recent transmitted or received characters. In
Delphi 1, the queue can hold at most 32760 entries. In 32-bit compilers, the queue can hold
up to 4 million entries.
The queue can be dumped to a text file at any time. This text file is a report of all data
transmitted and received by the application in the following format:
Transmit:
**[24]B0100000027fed4[13][138][17]
Receive:
rz[13]**[24]B00800000000dd38[13][138][17]
7
Transmit:
**[24]B0100000027fed4[13][138][17]
8
Receive:
[17]*[24]C[4][1][0][0][0][184]6[30][139]a.txt[0]6048
4734111064 0 0 3 18144[0][24]kP[251]B6
9
10
11
12
13
14
15
This report happens to be the first few exchanges of a Zmodem protocol transfer. Printable
characters are displayed as ASCII strings; non-printable characters are displayed as decimal
values in square brackets (e.g., [13] is a carriage return) or optionally as hex values.
Notice that the data is grouped in blocks of received and transmitted characters
representing the sequence in which the program made calls to GetChar and PutChar. A
new block is created whenever the program switches from transmitting to receiving or vice
versa. For example, if a program transmitted a continuous stream of data without ever
stopping to receive data, the report would consist of a single transmit block. Conversely, if
a program contained a character-by-character terminal loop, each block might consist of
only one character because the program constantly switched between sending and receiving
single characters.
The sequence in a trace report is not necessarily the same as the sequence in which data
arrived or departed from the serial port. Suppose that a program transmits the string
“ABCD” to a remote device that echoes the characters it receives. In a chronological report,
the echo characters received by the transmitter would be intermingled with, but slightly
behind, the transmitted characters.
16
17
34 Chapter 2: Port Component
1
1
That’s not what tracing was designed to do. It was designed to show the data in the order it
was processed by an application. The major benefit to such ordering is that it permits
checking program logic against the data the program is actually processing. For example,
tracing was extremely valuable during the development of the modem and file transfer
protocol portions of Async Professional. If a particular test went awry, it was a simple matter
to review the trace and find out what went wrong (of course, finding out why it went wrong
was another matter).
The mechanics of using tracing are quite simple. A typical use would look something like
the following:
...create comport component
ApdComPort.TraceSize := 1000;
ApdComPort.Tracing := tlOn;
...use comport component
ApdComPort.TraceName := 'TEST.TRC';
ApdComPort.Tracing := tlDump;
...destroy comport component
1
2
3
4
6
The state of the tracing facility is controlled by setting the Tracing property to one of the
following values shown in Table 2.1.
7
8
Table 2.1: Tracing property values
9
Value
Effect
tlOff
Turns off tracing without saving the trace data.
tlOn
Turns tracing on or resumes tracing after a pause.
tlDump
Writes the trace data to a new file, turns off tracing.
tlAppend
Appends the trace to an existing file, turns off tracing.
tlClear
Clears the trace buffer but leaves tracing on.
tlPause
Pauses tracing.
10
11
12
When inspected, either at run time or design time, Tracing will always be one of tlOff, tlOn,
or tlPause. Setting Tracing to any other value causes an action (such as writing the trace file)
then sets Tracing to either tlOff or tlOn. For example, setting Tracing to tlDump writes the
trace data to a new file, then turns Tracing off, so inspecting Tracing immediately after
setting it to tlDump would return tlOff.
13
14
15
16
17
TApdComPort Component 35
1
1
1
2
3
4
5
6
7
8
9
10
After you create a comport component, set the TraceSize property to specify the trace buffer
size, then set the Tracing property to tlOn to begin tracing. From then on, every successful
call to PutXxx and GetXxx is automatically recorded. If more than the specified number of
trace entries is generated, the queue always contains the most recent. Set the TraceName
property to the name of the file where the trace file should be stored. Optionally, you can set
the TraceHex property to True to enable writing of non-printable characters to the trace file
in hexadecimal format. When Tracing is set to tlDump, the current contents of the trace
buffer are written to disk.
Dispatch logging
Tracing is a great tool for examining the incoming and outgoing data processed by your
program. One of tracing’s major strengths is that it shows only the data processed when your
program calls the GetXxx and PutXxx routines. Comparing the resulting trace file to your
program logic can often point out logic errors.
In some situations, however, a more appropriate debugging tool is one that shows the true
chronology of incoming and outgoing data. It may be more important to see exactly when
data arrived at the port rather than seeing how your program processed that data.
The standard Windows communications driver doesn’t provide enough information to
determine exactly when data arrived. The next best thing is knowing when the Async
Professional internal dispatcher got the data, and that’s how “dispatch logging” works.
Dispatch logging creates an audit trail of each action taken by Async Professional
components. These entries are stored in a circular queue of a specified size. Since the queue
is circular, it contains information about the most recent transmitted or received characters.
Entries in this queue are of variable length, and the queue can be as large as 16 million bytes.
11
12
13
14
15
16
17
36 Chapter 2: Port Component
1
1
The queue can be dumped to a text file at any time. This text file is a report of all dispatcher
events in the following format:
APRO v4.00
Compiler : Delphi 6
Operating System : Windows NT 4.0
Time
Type
SubType
-------- -------- -----------00000010 TrDatChg Avail
00000010 TrgHdAlc Window
00000010 TrgHdAlc Window
00000010 TrDatChg Avail
00000010 TrgHdAlc Procedure
00000010 TrDatChg Avail
00000010 TrigAllc Data
00000010 TrigAllc Data
00000010 TrigAllc Data
00000010 TrigAllc Data
00000010 TrigAllc Status
1
2
Service Pack 4
Data
OtherData
-------- --------00000001
7DDE03CE
870302A2
00000001
00000000
00000001
00000008
rz[0D]
00000010
[05]
00000018
[10]
00000020
[1B]I
00000029
(Modem status)
The first three lines are the header of the text file a provide the installed version of Async
Professional, the current compiler, and the current operating system.
The first column of the report is a timestamp. It represents the time elapsed from the time
dispatch logging was turned on to when the entry was made, measured in milliseconds. The
multimedia API TimeGetTime is used to calculate this timestamp, so it should be accurate
to the nearest millisecond.
The second column is the major category of log entry, the third column identifies the log
entry subtype, the forth column provides additional information related to the event (often
a handle or data count), and the remaining column adds any additional information that
could be useful for the event being logged.
3
4
6
7
8
9
10
11
12
13
14
15
16
17
TApdComPort Component 37
1
1
1
2
3
4
5
6
7
8
9
The following tables identify possible entries in a dispatch log:
Log entry type: Dispatch
An entry of this type means that a communications event is being processed.
Subtype - Meaning
Data
Other Data
ReadCom - The Windows
comm driver has notified
APRO that incoming data
is available and APRO’s
dispatcher has, in turn,
read the available data.
The number of bytes
read.
The actual data.
WriteCom - APRO has sent
data to the Windows comm
driver.
The number of bytes
sent.
The actual data.
The numeric value of
the event received.
A translation of the
numeric value. (DCTS,
DDSR, TERI, DDCD, CTS,
DSR, RI, DCD.)
Line status - A line
status event has been
received from the
Windows comm driver.
Modem status - A modem
status event has been
received from the
Windows comm driver.
10
11
12
13
14
15
16
17
38 Chapter 2: Port Component
1
1
Log entry type: Trigger
1
A trigger is being dispatched by the dispatcher.
Subtype - Meaning
Data
Avail - A data avail
event is being
dispatched.
The number of bytes
ready to be read.
Timer - A timer event is
being dispatched.
The handle of the
trigger.
Data - A data trigger
match event is being
dispatched.
The handle of the
trigger.
Status - A status
trigger is being
dispatched.
The handle of the
trigger.
Other Data
2
3
4
6
Log entry type: Thread
7
Occur only if the DebugThreads define is enabled in AWUSER.PAS. They are designed to
provide detail about the operation of APRO’s threads.
8
Subtype - Meaning
Start - One of the three background threads associated with each comport
(dispatcher thread, communications thread, output thread) is starting.
9
Exit - One of the three background threads associated with each is
terminating.
10
Sleep - One of the three background threads associated with each is
entering a wait state.
11
Wake - One of the three background threads associated with each is
returning from a wait state.
12
13
14
15
16
17
TApdComPort Component 39
1
1
Log entry type: TrigAllc
1
A trigger is being allocated.
2
Subtype - Meaning
Data
Other Data
Data - A data trigger is being
allocated.
The handle
of the
trigger.
What the trigger is
being set to trigger
on.
Timer - A timer trigger is being
allocated.
The handle
of the
trigger.
Status - A status trigger is being
allocated.
The handle
of the
trigger.
3
4
5
6
The type of the status
trigger.
Log entry type: TrigDisp
A trigger is being disposed.
7
8
9
Subtype - Meaning
Data
Data - A data trigger is being deleted.
The handle of the trigger.
Timer - A timer trigger is being deleted.
The handle of the trigger.
Status - A status trigger is being deleted.
The handle of the trigger.
Log entry type: TrgHdAlc
10
11
12
13
A trigger handler has been allocated.
Subtype - Meaning
Data
Window - A window handle based trigger handler is
being registered with the comport.
The window handle.
Procedure - A procedure pointer based trigger
handler is being registered with the comport.
Method - A method pointer based trigger handler is
being registered with the comport.
14
15
16
17
40 Chapter 2: Port Component
1
1
Log entry type: TrgHdDsp
1
A trigger handler has been disposed.
Subtype - Meaning
Data
Window - A window handle based trigger handler is
being deregistered from the comport.
The window handle.
2
3
Procedure - A procedure pointer based trigger
handler is being deregistered from the comport.
4
Method - A method pointer based trigger handler is
being deregistered from the comport.
Log entry type: TrDatChg
Data associated with the trigger has been changed.
6
Subtype - Meaning
Data
Other Data
Avail - The data trigger length
value is being changed.
The new
length.
Timer - The time-out value for a
particular timer trigger is being
changed.
The handle
of the
timer
trigger.
The new time.
Status - SetStatusTrigger is being
called for a particular status
trigger.
The handle
of the
trigger
being
changed.
The new value mask for
the trigger.
7
8
9
10
11
12
13
14
15
16
17
TApdComPort Component 41
1
1
Log entry type: Telnet
1
Logs the telnet negotiation conversation during a Winsock telnet session. Each SubType
shows the type of negotiation being logged.
2
3
Subtype - Meaning
Data
Other Data
Sent WILL - APRO is acknowledging
that it will support a requested
mode.
The numeric
value of
the command
being
negotiated.
A translation of the
numeric command.
Sent WON’T - APRO is refusing to
support a requested mode.
The numeric
value of
the command
being
negotiated.
A translation of the
numeric command.
Sent DO - APRO is requesting the
support of a telnet mode.
The numeric
value of
the command
being
negotiated.
A translation of the
numeric command.
Sent DON’T - APRO is requesting
that a telnet mode not be
supported.
The numeric
value of
the command
being
negotiated.
A translation of the
numeric command.
Recv WILL - The telnet host is
acknowledging that it will support
a requested mode.
The numeric
value of
the command
being
negotiated.
A translation of the
numeric command.
Recv WON’T - The telnet host is
refusing to support a requested
mode.
The numeric
value of
the command
being
negotiated.
A translation of the
numeric command.
Recv DO - The telnet host is
requesting the support of a telnet
mode.
The numeric
value of
the command
being
negotiated.
A translation of the
numeric command.
4
5
6
7
8
9
10
11
12
13
14
15
16
17
42 Chapter 2: Port Component
1
1
Log entry type: Telnet (cont.)
Subtype - Meaning
Data
Other Data
Recv DON’T - The telnet host is
requesting that a telnet mode not
be supported.
The numeric
value of
the command
being
negotiated.
A translation of the
numeric command.
Command - A subnegotiation command
has been received.
The numeric
value of
the command
being
negotiated.
The collected command.
APRO currently doesn’t
support any of these
commands, but they are
logged nonetheless.
Sent Term - A string identifying
the terminal emulation type has
been sent to the host.
The numeric
value of
the command
being
negotiated.
The string sent.
1
2
3
4
6
7
Log entry type: Packet
Events that indicate state changes in packets.
8
Subtype - Meaning
Other Data
Enable - The packet is being enabled.
The name of the packet
component.
If the end condition was a string, the next log entry is another
StringPacket event with the value of the end string in the “other data”
column. If the end condition was a size event, the next log entry is a
SizePacket event.
Disable - The packet is being enabled.
The name of the packet
component.
StringPacket - A string packet event is
being dispatched.
The name of the packet
component.
If the end condition was a string, the next log entry is another
StringPacket event with the value of the end string in the “other data”
column. If the end condition was a size event, the next log entry is a
SizePacket event.
SizePacket - Describes the end value for a
StringPacket.
The value of the end
condition of the previously
listed StringPacket.
PcktTimeout - A packet timeout event is
being generated.
9
10
11
12
13
14
15
16
17
TApdComPort Component 43
1
1
1
Log entry type: Packet (cont.)
Subtype - Meaning
Other Data
2
StartStr - Describes the start string for
a particular packet.
The value of the start
string of an enable
sequence.
3
The event is always generated as a part of the enable sequence for the
packet, if it has a start string. See the Enable event above.
4
EndStr - Describes the current end string
for a particular packet.
5
6
7
8
The end string of an enable
sequence.
The event is always generated as a part of the enable sequence for the
packet, if it has an end string. See the Enable event above.
Idle - The packet is not currently
collecting data and is not waiting for a
string.
Waiting - The packet is waiting for its
start string to come in.
Collecting - The packet’s start condition
has been met and thus the packet currently
has ownership to the incoming data.
Log entry type: Error
9
10
The dispatcher was called recursively. This is an error and can cause events to be missed.
The cause is usually that event handlers take too long to process their data. No SubTypes
exist for this type of entry.
Log entry type: Fax
11
Entries for this Type track APRO’s progress through the send or receive fax state machine.
The SubType indicates the current state of the state machine.
12
Log entry type: XModem
13
14
15
16
Entries for this Type track APRO’s progress through the send or receive xmodem protocol
state machine. The SubType indicates the current state of the state machine.
Log entry type: YModem
Entries for this Type track APRO’s progress through the send or receive ymodem protocol
state machine. The SubType indicates the current state of the state machine.
Log entry type: ZModem
Entries for this Type track APRO’s progress through the send or receive zmodem protocol
state machine. The SubType indicates the current state of the state machine.
17
44 Chapter 2: Port Component
1
1
Log entry type: Kermit
Entries for this Type track APRO’s progress through the send or receive kermit protocol
state machine. The SubType indicates the current state of the state machine.
1
Log entry type: Ascii
2
Entries for this Type track APRO’s progress through the send or receive ASCII protocol state
machine. The SubType indicates the current state of the state machine.
3
Log entry type: BPlus
This type is currently not implemented due to an enumeration capacity limitation in the
C++ Builder 3 compiler.
4
Log entry type: User
A user defined event type. You can add custom strings to a comports logfile using the
comport’s AddStringToLog method. These strings have the type User in the log file.
6
Logging facility
The state of the logging facility is controlled by setting the Logging property to one of the
values shown in Table 2.2.
7
8
Table 2.2: Logging property values
Value
Explanation
tlOff
Turns off logging without saving the log data.
tlOn
Turns logging on or resumes logging after a pause.
tlDump
Writes the log data to a new file, turns off logging.
tlAppend
Appends the log to an existing file, turns off logging.
tlClear
Clears the log buffer but leaves logging on.
tlPause
Pauses logging.
See “Tracing” on page 33, for more information and a related example.
9
10
11
12
Example
13
This example shows how to construct and use a comport component. Create a new project,
add the following components, and set the property values as indicated in Table 2.3.
14
Table 2.3: Example project components and property values
15
Component
Property
Value
TApdComPort
ComNumber
<set as needed for your PC>
TButton
Name
‘Test’
16
17
TApdComPort Component 45
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
Double-click on the Test button and modify the generated method to match this:
procedure TForm1.TestClick(Sender : TObject);
begin
ApdComPort1.Output := 'ATZ'^M;
end;
This method transmits “ATZ” (the standard modem reset command), followed by a carriage
return, to the serial port opened by TApdComPort. If a modem is attached to the serial port
it should echo the command, then return “OK.”
Double-click on the TApdComPort OnTriggerAvail event handler in the Object Inspector
and modify the generated method to match this:
procedure TForm1.ApdComPort1TriggerAvail(
CP : TObject; Count : Word);
var
I : Word;
C : Char;
S : string;
begin
S := '';
for I := 1 to Count do begin
C := ApdComPort1.GetChar;
case C of
#0..#31 : {don't display} ;
else S := S + C;
end;
end;
ShowMessage('Got an OnTriggerAvail event for: ' + S);
end;
This method collects all of the received data into the string S, discarding non-printable
characters, then displays S using ShowMessage.
To run this program you need to attach a modem to the serial port, then compile and run
the project. Clicking on the Test button sends ‘ATZ’^M to the modem. The modem’s
responses are shown through one or more message boxes.
14
15
16
17
46 Chapter 2: Port Component
1
1
Hierarchy
1
TComponent (VCL)
! TApdBaseComponent (OOMisc) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
TApdCustomComPort (AdPort)
2
3
TApdComPort (AdPort)
4
Properties
AutoOpen
HWFlowOptions
Parity
BaseAddress
InBuffFree
PromptForPort
Baud
InBuffUsed
RI
BufferFull
InSize
RS485Mode
BufferResume
LineBreak
RTS
ComNumber
LineError
StopBits
CTS
LogAllHex
SWFlowOptions
DataBits
Logging
TapiMode
DCD
LogHex
TraceAllHex
DeltaCTS
LogName
TraceHex
DeltaDCD
LogSize
TraceName
DeltaDSR
ModemStatus
TraceSize
DeltaRI
Open
Tracing
DeviceLayer
OutBuffFree
UseEventWord
DSR
OutBuffUsed
DTR
Output
XOffChar
FlowState
OutSize
XOnChar
! Version
6
7
8
9
10
11
12
13
14
15
16
17
TApdComPort Component 47
1
1
1
2
3
4
5
6
7
Methods
ActivateDeviceLayer
FlushInBuffer
PutString
AddDataTrigger
FlushOutBuffer
RemoveAllTriggers
AddStatusTrigger
GetBlock
RemoveTrigger
AddStringToLog
GetChar
SendBreak
AddTimerTrigger
ProcessCommunications
SetBreak
AddTraceEntry
PutBlock
SetStatusTrigger
CharReady
PutChar
SetTimerTrigger
OnPortClose
OnTriggerData
OnTriggerOutbuffUsed
OnPortOpen
OnTriggerLineError
OnTriggerOutSent
OnTrigger
OnTriggerModemStatus
OnTriggerStatus
OnTriggerAvail
OnTriggerOutbuffFree
OnTriggerTimer
Events
8
9
10
11
12
13
14
15
16
17
48 Chapter 2: Port Component
1
1
Reference Section
ActivateDeviceLayer
1
virtual method
2
function ActivateDeviceLayer : TApdBaseDispatcher; virtual;
TApdBaseDispatcher = class;
3
! Called when the port is first opened to instantiate a device driver object for the port.
Async Professional includes several device layers. The dlWin32 device layer (the default for
32-bit applications) uses the standard Win32 comms drivers. The dlWin32 device layer have
a descendant device layer that is used if TapiMode is set to tmOn. The TAPI device layers
rely on TAPI to open and close the serial port, but are otherwise identical to their ancestor
device layers. The dlWinsock device layer is available only if you use TApdWinsockPort
component (see page 105).
4
6
You can create custom device layers by deriving them from TApdBaseDispatcher and
creating a new port descendant from TApdCustomComport where you override
ActivateDeviceLayer to return the newly defined device layer.
7
See “Device Independence” on page 725 for more information on device layers.
8
See also: DeviceLayer, TapiMode
AddDataTrigger
method
function AddDataTrigger(
const Data : string; const IgnoreCase : Boolean) : Word;
! Adds a string match trigger to the dispatcher.
9
10
11
Data is the string the dispatcher attempts to match in the received data stream. If IgnoreCase
is True, case is not considered when checking for a match.
12
If the trigger is added successfully, the function returns the handle of the trigger. Otherwise,
it generates an exception. No subsequent call is required to activate the trigger.
13
When the internal dispatcher finds incoming data that matches Data it generates an
OnTriggerData event.
14
The following example tells the internal dispatcher to generate an OnTriggerData event
whenever it receives the string “UserID:”. Because False is passed for IgnoreCase the case of
the strings must match exactly.
ApdComPort.AddDataTrigger('UserID:', False);
15
16
See also: AddStatusTrigger, AddTimerTrigger, RemoveTrigger
17
TApdComPort Component 49
1
1
1
AddStatusTrigger
method
function AddStatusTrigger(const SType : Word) : Word;
2
! Adds a status trigger of the specified type.
This method adds a status trigger of type SType, which is one of the following:
3
4
5
6
7
8
9
10
11
12
13
14
Value
Meaning
stModem
Trigger on modem status change.
stLine
Trigger on line status change.
stOutBuffFree
Trigger on output buffer free above level.
stOutBuffUsed
Trigger on output buffer used below level.
stOutSent
Trigger on any call to PutChar or PutBlock.
See the SetStatusTrigger method on page 88 or more information about these status trigger
types.
If the trigger is added successfully, the function returns the handle of the trigger; otherwise
it generates an exception. The trigger is not activated until a subsequent call to
SetStatusTrigger. The data associated with the trigger (such as the buffer free level for a
stOutBuffFree trigger event) is supplied at that time.
The following example adds a status trigger and enables the trigger to generate an
OnTriggerStatus event as soon as at least 100 bytes become free in the output buffer. Later
it deactivates the trigger but does not delete it. Note that status triggers are not selfrestarting; the application’s message handler must call SetStatusTrigger again once an event
is generated.
var
StatusHandle : Word;
...
StatusHandle := ApdComPort.AddStatusTrigger(stOutBuffFree);
ApdComPort.SetStatusTrigger(StatusHandle, 100, True);
...
ApdComPort.SetStatusTrigger(StatusHandle, 0, False);
See also: RemoveTrigger, SetStatusTrigger
15
16
17
50 Chapter 2: Port Component
1
1
AddStringToLog
method
1
procedure AddStringToLog(S : string);
! Adds a User-type log entry to the dispatcher log
2
This procedure can be called to add custom entries to the dispatcher log. These entries will
show up in the dispatcher log as “User” entries.
AddStringToLog can be very useful when debugging the application. Custom strings can be
added to the log to show when you process events, or start other operations, or just for
general logging purposes.
3
4
See also: Logging
AddTimerTrigger
method
6
function AddTimerTrigger : Word;
! Adds a timer trigger.
7
If the trigger is added successfully, the function returns the handle of the trigger; otherwise,
it generates an exception. The timer must be activated subsequently with a call to
SetTimerTrigger.
The following example adds a timer trigger and enables it to expire in 36 ticks (2 seconds),
when it will generate an OnTriggerTimer event. Note that timer triggers are not
self-restarting; the application’s event handler must call SetTimerTrigger again once an event
is handled.
var
Timer : Word;
...
Timer := ApdComPort.AddTimerTrigger;
ApdComPort.SetTimerTrigger(Timer, 36, True);
8
9
10
11
12
See also: SetTimerTrigger
13
14
15
16
17
TApdComPort Component 51
1
1
1
AddTraceEntry
method
procedure AddTraceEntry(const CurEntry, CurCh : Char);
2
3
4
5
6
7
8
9
10
! Adds a trace event to the port’s trace queue.
This procedure can be called to add a special entry to the trace buffer. The CurEntry
parameter indicates the type of entry, normally either ‘T’ for Transmit or ‘R’ for Receive.
CurCh is the character that was transmitted or received.
Although AddTraceEntry is intended primarily for internal use by Async Professional
routines that send and receive data, you can use it to store additional data in the trace buffer.
For example, you might add an entry just before you temporarily suspend tracing.
If CurEntry is a character other than ‘T’ or ‘R’, it shows up in the tracing report as shown in
the following code snippet where ‘X’ is the CurEntry character and ‘Y’ is CurCh:
Special-X:
Y
When AddTraceEntry is mixed in with a normal set of transmit and receive blocks, it looks
something like this:
Transmit:
ATZ[13]
Special-X:
Y
Receive:
ATZ[13][13[10]OK[13][10]
11
In this example the program transmitted ‘ATZ’<CR>, called AddTraceEntry(‘X’, ‘Y’), then
used one of the GetXxx routines to retrieve the received data.
12
See also: Tracing
AutoOpen
13
14
property
property AutoOpen : Boolean
Default: True
! Determines whether the port is automatically opened on demand.
15
16
If AutoOpen is True and a method or property that requires an open serial port is accessed,
the TApdComPort component automatically opens the port. If AutoOpen is False, the port
must be opened explicitly by setting the Open property to True.
See also: Open
17
52 Chapter 2: Port Component
1
1
BaseAddress
run-time property
1
property BaseAddress : Word
2
Default: 0
! Determines the base address of the port.
Under normal conditions, a program would not need to reference or set this property. The
base address has importance only when a program is using RS-485 hardware that uses RTS
to control the RS-485 line. In most cases Async Professional is able to determine the base
address automatically.
This property is provided for rare cases where Async Professional is not able
to automatically determine the base address of the RS-485 port. In these cases, this
property must be set manually in order for Async Professional to provide RTS line control
for the port.
See “RS-485 support overview” on page 31 for more information on RS-485 support.
See also: RS485Mode
Baud
property
property Baud : LongInt
3
4
6
7
8
9
Default: 19200
! Determines the baud rate used by the port.
10
Generally acceptable values for Baud include 300, 1200, 2400, 4800, 9600, 19200, 38400,
57600, and 115200.
If the port is open when Baud is changed, the line parameters are updated as soon as any
data existing in the output buffer has drained. Baud does not validate the assigned value
before passing it on to the communications driver. The driver may reject the value, leading
to an exception.
You can enter a baud rate using the Object Inspector or invoke the SelectBaudRate property
editor, which provides a drop-down list box of standard baud rates.
See also: ComNumber, DataBits, Parity, StopBits
11
12
13
14
15
16
17
TApdComPort Component 53
1
1
1
BufferFull
property
property BufferFull : Word
2
Default: 0
! Determines the input buffer level at which receive flow control is imposed.
3
4
5
6
7
8
9
10
11
When hardware flow control is used, BufferFull should typically be set to 90% of the input
buffer size. When software flow control is used, it should typically be set to 75% of the buffer
size, since it may take the remote some time to receive the XOff character and stop sending.
If flow control is enabled and BufferFull has not been set, or has been set to an invalid value,
the level is set to 90% of the input buffer size.
See also: BufferResume, HWFlowOptions, SWFlowOptions
BufferResume
property BufferResume : Word
Default: 0
! Determines the input buffer level at which receive flow control is deactivated.
When hardware flow control is used, BufferResume should typically be set to 10% of the
input buffer size. When software flow control is used, it should typically be set to 25% of the
buffer size, since it may take the remote some time to receive the XOn character and start
sending again. If flow control is enabled and BufferResume has not been set, or has been set
to an invalid value, the level is set to 10% of the input buffer size.
See also: BufferFull, HWFlowOptions, SWFlowOptions
CharReady
12
13
14
15
! Returns True if at least one character is in the dispatcher buffer.
Don’t use CharReady in OnTriggerAvail event handlers. Doing so can interfere with the
dispatcher’s data tracking ability, leading to errors or lost data. This function is provided for
the rare cases where you need to call GetChar outside of an event handler.
See also: GetChar
17
54 Chapter 2: Port Component
1
method
function CharReady : Boolean;
16
1
property
ComNumber
property
1
property ComNumber : Word
2
Default: 0
! Determines the serial port number (Com1, Com2, ...) used by the TApdComPort
component.
3
ComNumber does not validate the port number. When the port is opened, the Windows
communications driver will determine whether the port number is valid and generate an
error if it is not. To validate the port, use the IsPortAvailable method described on page 896.
4
If the port is open when ComNumber is changed, the existing port is closed and then
reopened using the new number. Triggers are maintained during this operation.
This property is ignored when the TAPI and Winsock device layers are used.
6
The following example creates, configures, and opens a comport component at run time:
7
ApdComPort := TApdComPort.Create(Self);
ApdComPort.ComNumber := 1; {use Com1}
ApdComPort.Baud := 9600;
ApdComPort.Parity := pNone;
ApdComPort.DataBits := 8;
ApdComPort.StopBits := 1;
ApdComPort.Open := True;
CTS
8
9
read-only, run-time property
10
property CTS : Boolean
11
! Returns True if the port’s “clear to send” line (CTS) is set.
The following example transmits a large block of data after assuring that the remote has
raised the CTS signal:
if ApdComPort.CTS then
ApdComPort.PutBlock(BigBlock, 1024);
12
13
See also: DSR
14
15
16
17
TApdComPort Component 55
1
1
1
DataBits
property
property DataBits : Word
2
Default: 8
! Determines the number of data bits of the port.
3
4
Acceptable values are 5, 6, 7, and 8.
If the port is open when DataBits is changed, the line parameters are updated immediately.
DataBits does not validate the assigned value before passing it on to the communications
driver. The driver may reject the value, leading to an exception.
5
See also: Baud, ComNumber, Parity, StopBits
6
DCD
read-only, run-time property
property DCD : Boolean
7
8
9
10
11
12
! Returns True if the port’s “data carrier detect” line (DCD) is set.
DCD is usually set only for serial connections made through a modem. Your modem sets
DCD to indicate that it has a connection with another modem. If either modem hangs up or
the connection is lost for another reason, your modem clears DCD (assuming it is
configured to do so.) Hence, if your application uses a modem connection, you might want
to check DCD periodically to assure that the connection is still valid or, better yet, use a
modem status trigger for the same purpose.
The following example detects carrier loss and handles the error:
if not ApdComPort.DCD then
{handle unexpected disconnect}
See also: DeltaDCD
DeltaCTS
13
14
15
property DeltaCTS : Boolean
! Returns True if the port’s “delta clear to send” bit (DeltaCTS) is set.
DeltaCTS is set only if CTS has changed since the last time the application read the value of
DeltaCTS.
See also: CTS
16
17
56 Chapter 2: Port Component
1
1
read-only, run-time property
DeltaDCD
read-only, run-time property
1
property DeltaDCD : Boolean
! Returns True if the port’s “delta data carrier detect” bit (DeltaDCD) is set.
DeltaDCD is set only if DCD has changed since the last time the application read the value
of DeltaDCD.
2
3
See also: DCD
DeltaDSR
read-only, run-time property
4
property DeltaDSR : Boolean
! Returns True if the port’s “delta data set ready” bit (DeltaDSR) is set.
DeltaDSR is set only if DSR has changed since the last time the application read the value
of DeltaDSR.
7
See also: DSR
DeltaRI
6
read-only, run-time property
8
property DeltaRI : Boolean
9
! Returns True if the port’s “delta ring indicator” bit (DeltaRI) is set.
DeltaRI is set only if RI (the ring indicator) has changed since the last time the application
called CheckDeltaRI.
10
The formal name for this bit is “trailing edge ring indicator” or TERI. DeltaRI is used for
consistency with other Async Professional naming conventions.
11
It is generally better to use DeltaRI to detect incoming calls than to use RI. RI toggles rapidly
as rings are detected, making it easy for your application to miss the brief periods that it
returns True. By contrast, DeltaRI returns True if any ring was detected since the last call to
the routine.
The following example calls the Answer method of TApdModem to connect a modem after
a ring is detected:
if ApdComPort.DeltaRI then
Modem.Answer;
12
13
14
15
See also: RI
16
17
TApdComPort Component 57
1
1
1
DeviceLayer
property
property DeviceLayer : TDeviceLayer
2
TDeviceLayer = (dlWin16, dlFossil, dlWin32, dlWinsock);
Default: dlWin16 for 16-bit, dlWin32 for 32-bit
3
4
5
6
7
8
9
! Determines the hardware interface used by the port.
Async Professional includes several device layers. The dlWin32 device layer (the default for
32-bit applications) uses the standard Win32 comms drivers. The dlWin32 device layer each
has a descendant device layer that is used if TapiMode is set to tmOn. The TAPI device layers
rely on TAPI to open and close the serial port, but are otherwise identical to their ancestor
device layers. The dlWinsock device layer is available only if you use TApdWinsockPort
component (see page 105).
You can create custom device layers by deriving them from TApdBaseDispatcher and
creating a new port descendant from TApdCustomComport where you override
ActivateDeviceLayer to return the newly defined device layer.
If desired, each individual TApdComPort component can use a different device layer.
See “Device Independence” on page 725 for more information on device layers.
See also: ActivateDeviceLayer
DSR
10
11
12
property DSR : Boolean
! Returns True if the port’s “data set ready” line (DSR) is set.
DSR is a signal that the remote device sets to indicate that it is attached and active. It may be
a good idea to check this signal before transmitting and periodically thereafter.
See also: DeltaDSR, CTS
13
14
15
16
17
58 Chapter 2: Port Component
1
1
read-only, run-time property
DTR
property
1
property DTR : Boolean
2
Default: True
! Determines the current state of the “data terminal ready” signal (DTR).
Some types of remote devices require that this signal be raised before they transmit. For
example, the default configuration of many modems is not to transmit data unless the PC
has raised the DTR signal.
3
4
The following example lowers the DTR signal after opening the port and later raises it again:
ApdComPort := TApdComPort.Create(Self);
ApdComPort.Open := True;
ApdComPort.DTR := False;
...
ApdComPort.DTR := True;
6
7
See also: RTS
FlowState
read-only, run-time property
8
property FlowState : TFlowControlState
TFlowControlState = (fcOff, fcOn, fcDsrHold, fcCtsHold,
fcDcdHold,fcXOutHold, fcXInHold, fcXBothHold);
! Returns the state of hardware or software flow control.
9
10
fcOff indicates that flow control is not in use. fcOn indicates that flow control is enabled, but
that blocking is not currently imposed in either direction.
fcDsrHold, fcCtsHold and fcDcdHold indicate that the application cannot transmit because
the other side has lowered DSR, CTS, or DCD respectively. Note that Async Professional
doesn’t currently provide DCD flow control.
Windows doesn’t provide information on the state of receive hardware flow control, so fcOn
is returned even if the local device is blocking received data by using a hardware flow control
line.
fcXOutHold indicates that the application cannot transmit because it has received an XOff
character from the remote. fcXInHold indicates that the application has sent an XOff
character to the remote to prevent it from transmitting data. fcXBothHold indicates that the
application has both sent and received an XOff.
11
12
13
14
15
16
17
TApdComPort Component 59
1
1
1
2
In the rare case where both hardware and software flow control are enabled for a port,
FlowState can return ambiguous results. In particular, if flow is blocked by both hardware
and software flow control, FlowState can return only the fact that one type is causing the
block.
See also: HWFlowOptions, SWFlowOptions
3
4
FlushInBuffer
method
procedure FlushInBuffer;
! Clears the input buffers used by both the Windows device driver and the Async Professional
5
internal dispatcher.
It also resets all data triggers so as to disregard any cleared data.
6
7
8
9
The following example flushes all data currently in the input buffer if a line error is detected.
You probably shouldn’t do this routinely after each line error. Logic like this is usually
appropriate only before trying to resynchronize with the transmitter in a file transfer
protocol.
if ApdComPort.LineError <> leNoError then begin
...error handling
ApdComPort.FlushInBuffer;
end;
See also: FlushOutBuffer
10
11
12
FlushOutBuffer
method
procedure FlushOutBuffer;
! Clears the output buffers used by both the Windows device driver and the Async
Professional internal dispatcher.
Any data pending in the output buffer is not transmitted.
13
14
15
16
The following example discards any data that hasn’t yet been transmitted whenever an
application function named ErrorDetected returns True after a remote device reports an
error:
if ErrorDetected then begin
ApdComPort.FlushOutBuffer;
...resync with remote
end;
See also: FlushInBuffer
17
60 Chapter 2: Port Component
1
1
GetBlock
method
1
procedure GetBlock(var Block; Len : Word);
! Returns a block of received characters and removes them from the dispatcher buffer.
This routine makes a request to return the next Len received bytes. The data is moved into
the buffer referenced by Block. If Block is not large enough to hold Len bytes this will result
in a memory overwrite. If fewer than Len bytes are available, none are returned and an
EBufferIsEmpty exception is generated. The returned bytes are removed from the Async
Professional dispatcher buffer.
2
3
4
To determine if line errors occurred while the communications driver was receiving this
data, check the LineError property after calling GetBlock.
The following example calls GetBlock to remove the next 128 bytes from the dispatcher
buffer, and handles the various possible outcomes:
var
Block : array[0..127] of Char;
...
try
ApdComPort.GetBlock(Block, 128);
except
on E : EAPDException do
if E is EBufferIsEmpty then begin
...protocol error, 128 bytes expected
raise;
end;
end;
6
7
8
9
10
11
See also: CharReady, GetChar, InBuffUsed, PeekBlock
12
13
14
15
16
17
TApdComPort Component 61
1
1
1
GetChar
method
function GetChar : Char;
2
! Returns the next character from the dispatcher buffer.
3
If at least one character is available in the dispatcher buffer, GetChar returns the first
available one.
4
To determine if line errors occurred while the communications driver was receiving this
data, check the LineError property after calling GetChar.
The following example returns the next available character in C:
5
6
var
C : Char;
...
if ApdComPort.CharReady then
C := ApdComPort.GetChar;
7
See also: CharReady, GetBlock, PeekChar
8
HWFlowOptions
property
property HWFlowOptions : THWFlowOptionSet
9
10
THWFlowOptionSet = set of THWFlowOptions;
THWFlowOptions = (
hwfUseDTR, hwfUseRTS, hwfRequireDSR, hwfRequireCTS);
Default: Empty set
11
! Determines the hardware flow control options for the port.
12
When the options are an empty set, as they are by default, there is no hardware flow control.
The options can be combined to enable hardware flow control.
13
“Receive flow control” stops a remote device from transmitting while the local input buffer
is too full. “Transmit flow control” stops the local device from transmitting while the remote
input buffer is too full.
14
15
16
Receive flow control is enabled by including the hwfUseRTS and/or hwfUseDTR elements in
the options set. When enabled, the corresponding modem control signals (RTS and/or
DTR) are lowered when the input buffer reaches the level set by the BufferFull property. The
remote must recognize these signals and stop sending data while they are held low. Because
there is usually little delay before the remote reacts (as there is with software flow control),
BufferFull can be set close to the input buffer size, perhaps at the 90% level.
17
62 Chapter 2: Port Component
1
1
As the application processes received characters, buffer usage eventually drops below the
value set by the BufferResume property. At that point, the corresponding modem control
signals are raised again. The remote must recognize these signals and start sending data
again. Again, because there is usually little delay you can set BufferResume close to zero,
perhaps at 10% of the input buffer size.
Transmit flow control is enabled by including the hwfRequireCTS and/or hwfRequireDSR
elements in the options set. With one or both of these options enabled, the Windows
communications driver doesn’t transmit data unless the remote device is providing the
corresponding modem status signal (CTS and/or DSR). The remote must raise and lower
these signals when needed to control the flow of transmitted characters.
1
2
3
4
Note that flow control using RTS and CTS is much more common than flow control using
DTR and DSR.
See “Flow Control” on page 711 for more information.
6
The following example enables bi-directional hardware flow control with limits at the 10%
and 90% levels of the buffer. RTS is lowered for receive flow control and CTS is checked for
transmit flow control. Later in the application, hardware flow control is disabled.
7
ApdComPort.HWFlowOptions := [hwfUseRTS, hwfRequireCTS];
ApdComPort.BufferFull := Trunc(0.9*ApdComPort.InSize);
ApdComPort.BufferResume := Trunc(0.1*ApdComPort.InSize);
...
ApdComPort.HWFlowOptions := [];
See also: DTR, FlowState, RTS, SWFlowOptions
InBuffFree
8
9
10
read-only, run-time property
11
property InBuffFree : Word
! Returns the number of bytes free in the dispatcher buffer.
12
This routine returns the number of bytes of free space in the Async Professional
dispatcher buffer. It does not tell you the free space in the Windows communications
driver input buffer.
13
Because the dispatcher automatically drains the Windows buffer using timer and
notification messages, its status is rarely relevant to the program.
14
The following example checks to see that there’s significant free space in the dispatcher
buffer before performing a time-consuming operation that doesn’t drain the buffer:
15
16
if ApdComPort.InBuffFree > 128 then
...perform a time-consuming operation
See also: InBuffUsed
17
TApdComPort Component 63
1
1
1
InBuffUsed
read-only, run-time property
property InBuffUsed : Word
2
3
! Returns the number of bytes currently available for reading from the dispatcher buffer.
This routine returns the number of bytes currently loaded in the Async Professional
dispatcher buffer. It does not include bytes in the Windows communications driver input
buffer that haven’t yet been moved to the dispatcher buffer.
4
Because the dispatcher automatically drains the Windows buffer using timer and
notification messages, this buffer’s status is rarely relevant to the program.
5
The following example checks InBuffUsed to see if received data is available for processing:
6
if ApdComPort.InBuffUsed <> 0 then
...process data
See also: CharReady, InBuffFree
7
8
InSize
property
property InSize : Word
Default: 4096
9
10
11
12
! Determines the size, in bytes, of the Window communications driver’s input buffer.
InSize should always be fairly large, perhaps 4096 or 8192. The larger this size, the less likely
the driver loses data if an ill-behaved program takes control of Windows foreground
processing for an extended time.
The Windows communication API does not permit changing the buffer size when a serial
port is open. When InSize is changed for an open port, the port is closed and re-opened
with the new size.
See also: Open, OutSize
13
LineBreak
14
property LineBreak : Boolean
15
! Returns True if a line break signal was received since the last call to LineBreak.
See also: OnTriggerStatus
16
17
64 Chapter 2: Port Component
1
1
read-only, run-time property
LineError
read-only, run-time property
1
property LineError : Word
! Returns a non-zero value if line errors have occurred since the last call to LineError.
It returns 0 if no errors were detected or the port is not yet open. Otherwise it returns a
numeric value from the following list that indicates the most severe pending error:
Value
Error Number
Meaning
leBuffer
1
Buffer overrun in COMM.DRV.
leOverrun
2
UART receiver overrun.
leParity
3
UART receiver parity error.
leFraming
4
UART receiver framing error.
leCTSTO
5
Transmit timeout waiting for CTS.
leDSRTO
6
Transmit timeout waiting for DSR.
leDCDTO
7
Transmit timeout waiting for RLSD.
leTxFull
8
Transmit queue is full.
2
3
4
6
7
8
Line errors can occur during calls to any GetXxx or PutXxx method of the port. If your
application must detect line errors, it should check LineError after each such call or group of
calls, or it should install an OnTriggerLineError event handler.
The following example checks for line errors after receiving data with GetBlock:
9
10
ApdComPort.GetBlock(DataBlock, DataLen);
if ApdComPort.LineError <> 0 then
...error handling
11
See also: OnTriggerLineError
LogAllHex
property
12
13
property LogAllHex : Boolean
Default: False
! Determines whether all characters in the dispatcher log are written as hex or decimal.
This property is useful when you are processing raw data, instead of a mixture of printable
text and raw data.
14
15
16
17
TApdComPort Component 65
1
1
1
If LogAllHex is False (the default), a received string of “123” will be written to the log as
literal, printable chars:
0000.824
2
3
4
Dispatch
ReadCom
0000000A
123
If LogAllHex is True, the same received string will be written to the log in their hexadecimal
notation:
0000.829
Dispatch
ReadCom
0000000A
[31][32][33]
See also: LogHex, TraceAllHex
Logging
5
property
property Logging : TTraceLogState
TTraceLogState = (tlOff, tlOn, tlDump, tlAppend, tlClear, tlPause);
6
7
Default: tlOff
! Determines the current logging state.
When Logging is set to tlOff, as it is by default, no logging is performed.
8
9
10
11
12
13
14
15
16
To enable logging, set Logging to tlOn. This allocates an internal buffer of LogSize bytes and
informs the dispatcher to start using this buffer. To disable logging without writing the
contents of the log buffer to a disk file, set Logging to tlOff. This also frees the internal buffer.
To write the contents of the logging buffer to disk, set Logging to tlDump (which overwrites
any existing file named LogName, or creates a new file) or tlAppend (which appends to
an existing file, or creates a new file). After the component writes to the file it sets Logging
to tlOff.
To clear the contents of the logging buffer and continue logging, set Logging to tlClear. After
the component clears the buffer, it sets Logging to tlOn.
To temporarily pause logging, set Logging to tlPause. To resume logging, set Logging
to tlOn.
See “Dispatch logging” on page 36 for more information.
The following example turns on logging and later dumps the logging buffer to APRO.LOG:
ApdComPort.Logging := tlOn;
...
ApdComPort.LogName := 'APRO.LOG';
ApdComPort.Logging := tlDump;
See also: AddStringToLog, LogHex, LogName, LogSize, Tracing
17
66 Chapter 2: Port Component
1
1
LogHex
property
1
property LogHex : Boolean
2
Default: True
! Determines whether non-printable characters stored in a dispatch logging file are written
3
using hexadecimal or decimal notation.
See also: LogAllHex, Logging, LogName, LogSize
LogName
property
4
property LogName : ShortString
Default: “APRO.LOG”
6
! Determines the name of the file used to store a dispatch log.
The dispatcher log file is written when the Logging property is changed to tlDump or
tlAppend. If a path is not provided, the log will be written to whatever directory is the
current directory, which may not be where you expect it to be. Specify an explicit path and
filename to ensure the log file is stored in the correct location.
7
8
See also: Logging
LogSize
property
9
10
property LogSize : Word
Default: 10000
! Determines the number of bytes allocated for the dispatch logging buffer.
The assigned value limit is 16 million. Each dispatch entry consumes at least 10 bytes. Many
entries use additional buffer space to store a sequence of received or transmitted characters.
This property should normally be set before a logging session begins. If a changed value is
assigned to LogSize while a logging session is active, the current session is aborted (which
clears all information from the logging buffer), the new buffer is allocated, and a new
logging session is started.
11
12
13
14
See also: Logging
15
16
17
TApdComPort Component 67
1
1
1
ModemStatus
read-only, run-time property
property ModemStatus : Byte
2
3
4
5
6
7
8
9
! Returns the modem status byte and clears all delta bits.
The status is returned in the byte format used by the UART’s modem status register. The
returned value can be used with the following bit masks to isolate the desired bits:
Mnemonic
Value
Description
DeltaCTSMask
01h
CTS changed since last read.
DeltaDSRMask
02h
DSR changed since last read.
DeltaRIMask
04h
RI changed since last read.
DeltaDCDMask
08h
DCD changed since last read.
CTSMask
10h
Clear to send.
DSRMask
20h
Data set ready.
RIMask
40h
Ring indicator.
DCDMask
80h
Data carrier detect.
You’ll probably find it easier to use the CTS, DSR, RI, DCD, and related DeltaXxx properties
instead of ModemStatus, but ModemStatus is more efficient when you need to check several
signals at once.
10
ModemStatus also clears the internal delta bits used to indicate changes in the CTS, DSR, RI,
and DCD signals. This affects the results of subsequent checks of DeltaCTS, etc.
11
The following example uses ModemStatus to check for dropped carrier. It would have been
simpler in this case to check the DCD property directly.
12
13
Status := ApdComPort.ModemStatus;
if Status and DCDMask = 0 then
...port dropped carrier
See also: CTS, DCD, DeltaCTS, DeltaDCD, DeltaDSR, DeltaRI, DSR, RI
14
15
16
17
68 Chapter 2: Port Component
1
1
OnPortClose
event
1
property OnPortClose : TNotifyEvent
! OnPortClose is generated when the serial port associated with the TApdComPort is
2
physically closed.
Setting the Open property to False does not close the associated serial port immediately.
Buffers and structures need to be reset, and the physical hardware needs to be closed before
the port can be considered closed. This event is generated when the physical port is actually
closed and available to other processes.
3
4
See also: OnPortOpen, Open
OnPortOpen
event
6
property OnPortOpen : TNotifyEvent
! OnPortOpen is generated when the serial port associated with the TApdComPort is
7
physically opened.
Setting the Open property to True begins a series of actions, ending with the physical serial
port being opened and ready for use. This includes creating the buffers, data structures and
the three threads required by the component. This process may take a few milliseconds,
depending on the responsiveness of the Windows serial port drivers, unconventional
configurations, etc. This event is generated when the physical serial port is opened and ready
for use.
See also: OnPortClose, Open
OnTrigger
event
property OnTrigger : TTriggerEvent
TTriggerEvent = procedure(
CP : TObject; Msg, TriggerHandle, Data : Word) of object;
! Defines an event handler that is called whenever any serial data trigger occurs.
OnTrigger can be used to handle all kinds of trigger events in one location. Normally it is
easier to use the more specific kinds of serial data triggers. The OnTrigger event handler is
always called first, then the more specific event handlers are also called if they are assigned.
8
9
10
11
12
13
14
15
16
17
TApdComPort Component 69
1
1
1
2
3
4
5
6
7
8
9
10
CP is the TApdComPort component that generated the trigger. Msg is the Windows message
that specifies the kind of trigger:
Trigger
Description
APW_TRIGGERAVAIL
Corresponds to OnTriggerAvail.
APW_TRIGGERDATA
Corresponds to OnTriggerData.
APW_TRIGGERTIMER
Corresponds to OnTriggerTimer.
APW_TRIGGERSTATUS
Corresponds to OnTriggerStatus.
TriggerHandle is the handle number returned when the trigger was added. Data is a
numeric value that is relevant for the APW_TRIGGERAVAIL, APW_TRIGGERDATA, and
APW_TRIGGERSTATUS events. See the corresponding event handlers for more
information.
The following example waits for and responds to a login prompt, processing
APW_TRIGGERDATA, APW_TRIGGERAVAIL, and APW_TRIGGERTIMER messages in
a single routine:
DataTrig := ApdComPort.AddDataTrigger('login:', True);
TimerTrig := ApdComPort.AddTimerTrigger;
ApdComPort.SetTimerTrigger(TimerTrig, 182, True);
...
procedure TMyForm.ApdComPortTrigger(
CP : TObject; Msg, TriggerHandle, Data : Word);
var
I : Word;
C : Char;
11
12
13
14
15
16
17
70 Chapter 2: Port Component
1
1
begin
case Msg of
APW_TRIGGERDATA :
{got 'login', send response}
ApdComPort.PutString('myname');
APW_TRIGGERAVAIL :
{extract and display/process the data}
for I := 1 to Data do begin
C := ApdComPort.GetChar;
...process data
end;
APW_TRIGGERTIMER :
{timed out waiting for login prompt, handle error}
...
end;
end;
1
2
3
4
6
See also: OnTriggerAvail, OnTriggerData, OnTriggerStatus, OnTriggerTimer
7
OnTriggerAvail
event
property OnTriggerAvail : TTriggerAvailEvent
8
TTriggerAvailEvent = procedure(
CP : TObject; Count : Word) of object;
9
! Defines an event handler that is called whenever a certain amount of serial input data is
available for processing.
10
This event is generated when data has been transferred into the dispatcher buffer.
CP is the TApdComPort component that generated the trigger. Count is the actual number
of bytes that are available to read at the instant the event is generated. Your event handler
should process exactly the number of bytes of input equal to the value of the Count
parameter (by calling GetChar or GetBlock). It should not use CharReady in a loop to
process all available bytes since additional bytes may have transferred into the buffer while
the event handler is active, and removing those bytes could interfere with the dispatcher’s
data tracking mechanism.
11
If several parts of the same application are using the same comport component and each
part installs its own OnTriggerAvail handler (as would occur if a terminal window and a file
transfer protocol were both in use), the Async Professional dispatcher takes pains to ensure
that all of them can read the same data. Even if the first handler to get control calls GetChar
to read Count bytes of data, the second and subsequent handlers will also be able to read the
same bytes of data by calling GetChar. After all of the OnTriggerAvail handlers have
returned, the dispatcher determines the largest number of characters read by any of the
handlers and removes those characters from the dispatcher buffer. If any data remains in the
14
12
13
15
16
17
TApdComPort Component 71
1
1
1
buffer, the dispatcher immediately generates another OnTriggerAvail event. Hence, if one
handler reads fewer characters than the other handlers, it will miss seeing the characters it
did not read on its first opportunity.
2
" Caution: Be sure to process the exact number of bytes passed in the Count parameter of this
3
handler. If you process fewer bytes, you risk losing characters to another component
extracting data during the event (such as the terminal). If you process more than Count
bytes, you risk receiving events for overlapping data – which may eventually lead to an
EBufferIsEmpty exception.
4
5
6
7
8
9
10
11
12
13
14
15
The following example collects incoming data until it finds a carriage return character
(ASCII 13). If the incoming data stream contained “TurboPower Software”<CR>,
ApdComPortTriggerAvail would be called one or more times until the entire string except
<CR> was received. ApdComPortTriggerData would then be called and could process the
complete string. ApdComPortTriggerAvail would then be called again with the <CR> and
any other data that followed it. ApdComPortTriggerData would not be called again in this
example, because the handler disables the data trigger.
const
S : string = '';
...
CRTrig := ApdComPort.AddDataTrigger(#13, False);
...
procedure TMyForm.ApdComPortTriggerData(
CP : TObject; TriggerHandle : Word);
begin
if TriggerHandle = CRTrig then begin
...do something with S
ApdComPort.RemoveTrigger(TriggerHandle);
end;
end;
procedure TMyForm.ApdComPortTriggerAvail(
CP : TObject; Count : Word);
var
I : Word;
begin
for I := 1 to Count do
S := S + ApdComPort.GetChar;
end;
See also: OnTrigger, OnTriggerData
16
17
72 Chapter 2: Port Component
1
1
OnTriggerData
event
1
property OnTriggerData : TTriggerDataEvent
2
TTriggerDataEvent = procedure(
CP : TObject; TriggerHandle : Word) of object;
! Defines an event handler that is called whenever a string matching a predefined goal is
3
detected in the input buffer.
The event is generated as a result of adding a match string using AddDataTrigger. When the
dispatcher finds a matching string in the input buffer, it generates an OnTriggerAvail event
for the bytes leading up to the match, then generates an OnTriggerData event for the match.
Finally, it generates another OnTriggerAvail event for the matched data itself.
4
CP is the TApdComPort component that generated the trigger. TriggerHandle is the handle
number returned when the trigger was added.
6
Note that data match triggers remain active until explicitly removed. The event handler can
call RemoveTrigger, passing TriggerHandle as the parameter, to remove the trigger that just
generated the event.
7
See also: AddDataTrigger, OnTrigger, OnTriggerAvail
8
OnTriggerLineError
event
9
property OnTriggerLineError : TTriggerLineErrorEvent
TTriggerLineErrorEvent = procedure(
CP : TObject; Error : Word; LineBreak : Boolean) of object;
! Defines an event handler that is called whenever the dispatcher detects a line error or line
break in the received data.
This event handler is called in a subset of the cases where the more general OnTriggerStatus
handler is called. OnTriggerStatus is called first when a line error is detected, even if an
OnTriggerLineError handler is installed.
CP is the TApdComPort component that generated the trigger. Error is a numeric code that
indicates the most severe line error detected. See the LineError property for details. The
LineBreak parameter is True if a line break was detected.
Note that status triggers are not self-restarting. The event handler must call SetStatusTrigger
again to reactivate the trigger as needed.
10
11
12
13
14
15
16
17
TApdComPort Component 73
1
1
1
2
3
4
5
6
7
8
9
The following example adds a status trigger for line errors and line breaks. The events are
handled using an OnTriggerLineError event handler.
TrigLE : Word;
...
TrigLE := ApdComPort.AddStatusTrigger(stLine);
ApdComPort.SetStatusTrigger(
TrigLE, lsParity or lsFraming or lsOverrun or lsBreak, True);
...
procedure TMyForm.ApdComPortTriggerLineError(
CP : TObject; Error : Word; LineBreak : Boolean);
begin
if Error <> leNone then
...process line error
if LineBreak then
...process line break
{reactivate trigger}
ApdComPort1.SetStatusTrigger(
TrigLE, lsParity or lsFraming or lsOverrun or lsBreak, True);
end;
See also: LineError, OnTriggerStatus, SetStatusTrigger
OnTriggerModemStatus
event
property OnTriggerModemStatus : TNotifyEvent
10
! Defines an event handler that is called whenever the dispatcher detects that modem status
signals have changed.
11
12
This event handler is called in a subset of the cases where the more general OnTriggerStatus
handler is called. OnTriggerStatus is called first when a modem status change is detected,
even if an OnTriggerModemStatus handler is installed.
14
The parameter passed to the TNotifyEvent is the TApdComPort component that generated
the trigger. The TApdComPort’s modem status properties can be checked by the event
handler to determine the exact reason for the event. No TriggerHandle is passed to the event
handler, so it is not possible to distinguish between multiple modem status triggers in this
event handler. If you need to do so, use the OnTriggerStatus event instead.
15
Note that status triggers are not self-restarting. The event handler must call SetStatusTrigger
again to reactivate the trigger as needed.
13
16
17
74 Chapter 2: Port Component
1
1
The following example adds and activates a modem status trigger for ring indicators and
changes in DSR. Modem status changes are handled using an OnTriggerModemStatus event
handler.
TrigMS : Word;
...
TrigMS := ApdComPort.AddStatusTrigger(stModem);
ApdComPort.SetStatusTrigger(
TrigMS, msRingDelta or msDSRDelta, True);
...
procedure TMyForm.ApdComPortTriggerModemStatus(CP : TObject);
begin
if ApdComPort.DeltaRI then
...handle ring
if ApdComPort.DeltaDSR then
...handle change in DSR
{reactivate trigger}
ApdComPort.SetStatusTrigger(
TrigMS, msRingDelta or msDSRDelta, True);
end;
See also: CTS, DCD, DeltaCTS, DeltaDCD, DeltaDSR, DeltaRI, DSR, ModemStatus, RI
OnTriggerOutbuffFree
event
1
2
3
4
6
7
8
9
property OnTriggerOutbuffFree : TNotifyEvent
! Defines an event handler that is called whenever the dispatcher detects that free space in its
10
output buffer has reached a certain level.
This event handler is called in a subset of the cases where the more general OnTriggerStatus
handler is called. OnTriggerStatus is called first when sufficient free space is detected, even if
an OnTriggerOutbuffFree handler is installed. If the output buffer level is already below the
specified level when the trigger is activated, an OnTriggerOutbuffFree event is generated the
next time the internal dispatcher gains control.
The parameter passed to the TNotifyEvent is the TApdComPort component that generated
the trigger.
Note that status triggers are not self-restarting. The event handler must call SetStatusTrigger
again to reactivate the trigger as needed.
11
12
13
14
15
16
17
TApdComPort Component 75
1
1
1
2
3
4
5
6
The following example adds and activates a status trigger for OnTriggerOutbuffFree events;
when the output buffer free level reaches 255 or greater ApdComPortTriggerOutbuffFree
transmits BigString:
TrigOBF : Word;
...
TrigOBF := ApdComPort.AddStatusTrigger(stOutbuffFree);
ApdComPort.SetStatusTrigger(TrigOBF, 255, True);
...
procedure TMyForm.ApdComPortTriggerOutbuffFree(CP : TObject);
begin
{buffer has at least 255 bytes free, transmit a big string}
ApdComPort.Output := BigString;
end;
See also: OnTriggerStatus, OnTriggerOutbuffUsed
OnTriggerOutbuffUsed
7
8
9
10
property OnTriggerOutbuffUsed : TNotifyEvent
! Defines an event handler that is called whenever the dispatcher detects that used space in its
output buffer has dropped below a certain level.
This event handler is called in a subset of the cases where the more general OnTriggerStatus
handler is called. OnTriggerStatus is called first when used space drops below a particular
level, even if an OnTriggerOutbuffUsed handler is installed. If the output buffer already
contains fewer bytes than the specified level when the trigger is activated, an
OnTriggerOutbuffUsed event is generated as soon as the internal dispatcher gains control.
11
The parameter passed to the TNotifyEvent is the TApdComPort component that generated
the trigger.
12
Note that status triggers are not self-restarting. The event handler must call SetStatusTrigger
again to reactivate the trigger as needed.
13
The following example adds and activates a status trigger for an OnTriggerOutbuffUsed
event; when the output buffer used level drops below 100 bytes
ApdComPortTriggerOutbuffUsed is called and transmits additional data:
14
15
16
TrigOBU : Word;
...
TrigOBU := ApdComPort.AddStatusTrigger(stOutBuffUsed);
ApdComPort.SetStatusTrigger(TrigOBU, 100, True);
...
procedure TMyForm.ApdComPortTriggerOutbuffUsed(CP : TObject);
17
76 Chapter 2: Port Component
1
1
event
begin
{buffer almost empty, start filling up again}
ApdComPort.Output := Stuff;
ApdComPort.Output := MoreStuff;
ApdComPort.Output := EvenMoreStuff;
...
end;
1
2
3
See also: OnTriggerStatus, OnTriggerOutbuffFree
OnTriggerOutSent
event
4
property OnTriggerOutSent : TNotifyEvent
! Defines an event handler that is called whenever the dispatcher gets a request to send one or
more characters.
6
This event handler is called in a subset of the cases where the more general OnTriggerStatus
handler is called. OnTriggerStatus is called first when an output request occurs, even if an
OnTriggerOutSent handler is installed. The parameter passed to the TNotifyEvent is the
TApdComPort component that generated the trigger.
Unlike most triggers, OnTriggerOutSent does not need to be reset. The event is always
generated for output events until the trigger is deactivated.
The following example adds a status trigger for OnTriggerOutSent; thereafter, each time the
program calls any transmit method or property ApdComPortTriggerOutSent is called to
update a status display:
TrigOS : Word;
...
TrigOS := ApdComPort.AddStatusTrigger(stOutSent);
...
procedure TMyForm.ApdComPortTriggerOutSent(CP : TObject);
begin
...update display to indicate data was transmitted
end;
7
8
9
10
11
12
13
See also: OnTriggerStatus
14
15
16
17
TApdComPort Component 77
1
1
1
OnTriggerStatus
event
property OnTriggerStatus : TTriggerStatusEvent
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
TTriggerStatusEvent = procedure(
CP : TObject; TriggerHandle : Word) of object;
! Defines an event handler that is called whenever a line status change of some kind is
detected.
This event handler combines the events described under OnTriggerLineError,
OnTriggerModemStatus, OnTriggerOutbuffFree, OnTriggerOutbuffUsed, and
OnTriggerOutSent.
CP is the TApdComPort component that generated the trigger. TriggerHandle is the handle
number returned when the trigger was added.
With the exception of OnTriggerOutSent, status triggers are not self-restarting. The event
handler must call SetStatusTrigger again to reactivate the trigger as needed.
The following example adds and activates status triggers for line errors and modem status
changes. Subsequent status events are handled by ApdComPortTriggerStatus.
TrigLE : Word;
TrigMS : Word;
...
TrigLE := ApdComPort.AddStatusTrigger(stLine);
TrigMS := ApdComPort.AddStatusTrigger(stModem);
ApdComPort.SetStatusTrigger(
TrigLE, lsParity or lsFraming or lsOverrun or lsBreak, True);
ApdComPort.SetStatusTrigger(
TrigMS, msRingDelta or msDSRDelta, True);
...
procedure TMyForm.ApdComPortTriggerStatus(
CP : TObject; TriggerHandle : Word);
begin
if TriggerHandle = TrigLE then begin
...handle line error or break
...reset line error trigger
end else if TriggerHandle = TrigMS then begin
...handle modem status change
...reset modem status trigger
end;
end;
See also: OnTriggerLineError, OnTriggerModemStatus, OnTriggerOutbuffFree,
OnTriggerOutbuffUsed, OnTriggerOutSent
17
78 Chapter 2: Port Component
1
1
OnTriggerTimer
event
1
property OnTriggerTimer : TTriggerTimerEvent
2
TTriggerTimerEvent = procedure(
CP : TObject; TriggerHandle : Word) of object;
! Defines an event handler that is called when an Async Professional timer expires.
CP is the TApdComPort component that generated the trigger. TriggerHandle is the handle
number returned when the trigger was added.
3
4
Note that timer triggers are not self-restarting. The event handler must call SetTimerTrigger
again to reactivate the trigger as needed.
The following example adds and activates two timer triggers. After 10 seconds and 60
seconds elapse, events are generated and handled by ApdComPortTriggerTimer.
Timer1, Timer2 : Word;
...
Timer1 := ApdComPort.AddTimerTrigger;
Timer2 := ApdComPort.AddTimerTrigger;
ApdComPort.SetTimerTrigger(Timer1, 182, True);
ApdComPort.SetTimerTrigger(Timer2, 1092, True);
...
procedure TMyForm.ApdComPortTriggerTimer(
CP : TObject; TriggerHandle : Word);
begin
if TriggerHandle = Timer1 then begin
...handle 10 second timeout condition
{restart timer}
ApdComPort.SetTimerTrigger(Timer1, 182, True);
end else begin
...handle 60 second timeout condition
{restart timer}
ApdComPort.SetTimerTrigger(Timer2, 1092, True);
end;
end;
See also: AddTimerTrigger, SetTimerTrigger
6
7
8
9
10
11
12
13
14
15
16
17
TApdComPort Component 79
1
1
1
Open
property
property Open : Boolean
2
Default: False
! Determines whether the physical port is opened and initialized with all current port
3
4
5
6
7
properties.
Open must be set to True before a comport component can send or receive characters. If the
AutoOpen property is set to True, the comport component will open itself automatically
under many conditions: calling any I/O method or property or when a component that uses
a TApdComPort is loaded.
When Open is set to True, the TApdComPort uses all current property settings to allocate
input and output buffers, open the physical port, initialize the line settings and flow control
settings, and enable or disable tracing and logging. It then registers a low-level trigger for the
port, which gets the first look at all trigger events and passes control on to the appropriate
OnTriggerXxx event handlers.
8
When Open is set to False, the TApdComPort turns off tracing and logging (by setting the
associated properties to tlDump, which creates an output file if any information has been
buffered), closes the physical port, and deallocates input and output buffers.
9
There is no harm done by setting Open to True when it is already True, or setting it to False
when it is already False.
10
See also: AutoOpen, OnPortClose, OnPortOpen
OutBuffFree
11
12
13
property OutBuffFree : Word
! Returns the number of bytes free in the output buffer.
Use OutBuffFree to assure that the output buffer has enough free space to hold data that you
are about to transmit.
14
15
16
17
80 Chapter 2: Port Component
1
1
read-only, run-time property
The following example checks for sufficient output buffer space to transmit a block of
NeededSpace bytes. If enough space is available the block is transmitted. Otherwise, a status
trigger is added to detect the required free space. The code assumes that an OnTriggerStatus
event handler has already been activated.
if ApdComPort.OutBuffFree >= NeededSpace then
ApdComPort.PutBlock(Data, NeededSpace)
else begin
MyHandle := ApdComPort.AddStatusTrigger(stOutBuffFree);
ApdComPort.SetStatusTrigger(MyHandle, NeededSpace, True);
end;
1
2
3
4
See also: OutBuffUsed
OutBuffUsed
read-only, run-time property
6
property OutBuffUsed : Word
! Returns the number of bytes currently in the output buffer.
7
Use OutBuffUsed to detect whether or not any outgoing data remains in the output buffer.
The following example checks to see if any outgoing data is still in the output buffer. If so, it
sets a status trigger to go off once the buffer is completely empty. The code assumes that an
OnTriggerStatus event handler has already been activated.
if ApdComPort.OutBuffUsed <> 0 then begin
MyHandle := ApdComPort.AddStatusTrigger(stOutBuffUsed);
ApdComPort.SetStatusTrigger(MyHandle, 0, True);
end;
9
10
11
See also: OutBuffFree
Output
8
write-only, run-time property
12
property Output : string
! Transmits its assigned value through the port.
13
Assigning a value to Output is equivalent to calling the PutString method with that same
string.
14
The following example sends a dial string out the port:
15
ApdComPort.Output := 'ATDT555-1212'^M;
See also: PutChar, PutString
16
17
TApdComPort Component 81
1
1
1
OutSize
property
property OutSize : Word
2
Default: 4096
! Determines the size, in bytes, of the output buffer used by the Windows communications
3
4
5
6
driver.
OutSize must be large enough to hold the largest block of data that you might transmit at
one time (using PutBlock, for example). For file transfer protocols OutSize must be at least
2078 bytes. The recommended setting is 4096, which is large enough to work with all
protocols but not so large that it is wasteful.
To obtain a non-default buffer size, OutSize must be set before the port is opened.
See also: Open, InSize
Parity
property
7
property Parity : TParity
8
TParity = (pNone, pOdd, pEven, pMark, pSpace);
Default: pNone
9
! Determines the parity checking mode of the port.
10
If the port is open when Parity is changed, the line parameters are updated immediately.
Parity does not validate the assigned value before passing it on to the communications
driver. The driver may reject the value, leading to an exception.
11
See also: Baud, ComNumber, DataBits, StopBits
12
ProcessCommunications
method
procedure ProcessCommunications;
13
14
15
! Calls the internal dispatcher one time.
This method is used by the Winsock device layer.
An application should call this routine if it needs to receive data during lengthy processing
where the application’s message loop isn’t running, or the dispatcher thread is otherwise
blocked.
16
17
82 Chapter 2: Port Component
1
1
The internal dispatcher, which is responsible for retrieving data from the Windows
communication driver, is normally called from an application’s message loop. If an
application isn’t calling its message loop then no new received data will be retrieved. To
retrieve new data the application must call ProcessCommunications, usually in a loop, until
it receives its data or times out.
Note that ProcessCommunications is provided for those cases where an application must
wait (for timing reasons) for a particular response. Normally, an application would use
OnTriggerAvail and OnTriggerData event handlers to wait for data.
The following example sends a string and waits for a response:
ET : EventTimer;
S : string;
...
S := '';
ApdComPort.Output := 'login:';
NewTimer(ET, 182);
repeat
ApdComPort.ProcessCommunications;
if ApdComPort.CharReady then
S := S + ApdComPort.GetChar;
until (S = 'ABC') or TimerExpired(ET);
1
2
3
4
6
7
8
See also: AddDataTrigger
9
PromptForPort
property
10
property PromptForPort : Boolean
Default: True
11
! Indicates whether the user should be prompted for the serial port number.
If PromptForPort is True and ComNumber is zero, a dialog is displayed to prompt the user
for the serial port when the port is opened:
If PromptForPort is False and ComPort is zero, an ENoPortSelected exception is raised
when the port is opened. This is the same behavior as older versions of Async Professional,
which do not have a PromptForPort property.
12
13
14
See also: ComPort
15
16
17
TApdComPort Component 83
1
1
1
PutBlock
method
function PutBlock(const Block; const Len : Word) : Integer;
2
! Copies a block of data to the output buffer of the Windows communications driver.
The communications driver then transmits the block byte-by-byte as fast as possible.
3
4
When there is insufficient free space in the output buffer, the documented behavior of the
Windows communications driver is to delete old data from the buffer. To avoid this
behavior, programs should always check OutBuffFree before calling any PutXxx method or
assigning a value to the Output property.
5
Block refers to the block of data and Len is the number of bytes to transmit. Len must be
smaller than the current value of the OutSize property.
6
The following example transmits a block of 20 characters after assuring that space is
available:
7
8
if ApdComPort.OutBuffFree >= 20 then
ApdComPort.PutBlock(Block, 20);
See also: OutBuffFree, PutChar, PutString
PutChar
9
10
method
procedure PutChar(const C : Char);
! Copies a single character to the output buffer of the Windows communications driver.
The communications driver then transmits the character as soon as possible.
11
12
The following example transmits one character after assuring that space is available:
if ApdComPort.OutBuffFree >= 1 then
ApdComPort.PutChar(C);
See also: OutBuffFree, PutBlock, PutString
13
14
PutString
method
procedure PutString(const S : string);
! Copies a string to the output buffer of the Windows communications driver.
15
The communications driver then transmits the string as soon as possible. The length byte of
the string is not transmitted.
16
17
84 Chapter 2: Port Component
1
1
The following example transmits a string after assuring that space is available:
1
S := 'Guinness Stout';
if ApdComPort.OutBuffFree >= Length(S) then
ApdComPort.PutString(S);
2
See also: OutBuffFree, Output, PutBlock, PutChar
RemoveAllTriggers
method
3
4
procedure RemoveAllTriggers;
! Deactivates all triggers added to this port.
Use this routine when your program changes modes and requires completely new triggers.
Destroying or closing a port automatically removes all of its triggers.
" Caution: Calling this method effectively disables the comport component since it removes
6
all triggers, including the ones that Async Professional requires internally for normal
operation of the comport and associated components.
7
Normally, it’s best to keep track of the triggers you add and remove them individually using
RemoveTrigger when they are no longer needed.
8
See also: Open, RemoveTrigger
RemoveTrigger
method
procedure RemoveTrigger(Handle : Word);
9
10
! Deactivates a specified trigger.
Handle is the handle returned when the trigger was added. If no matching trigger handle is
found, no error is generated. If Handle is a valid trigger, Handle is set to zero when removed.
11
The following example adds and uses a timer trigger, and later removes it:
12
var
MyHandle : Word;
...
MyHandle := ApdComPort.AddTimerTrigger;
ApdComPort.SetTimerTrigger(MyHandle, 36, True);
...
ApdComPort.RemoveTrigger(MyHandle);
13
14
15
See also: RemoveAllTriggers
16
17
TApdComPort Component 85
1
1
1
RI
read-only, run-time property
property RI : Boolean
2
3
! Returns True if the port’s “ring indicator” line (RI) is set.
Because the ring indicator line fluctuates rapidly as rings occur, the DeltaRI property is
much more reliable for detecting an incoming call.
See also: DeltaRI
4
5
RS485Mode
property
property RS485Mode : Boolean
Default: False
6
! Determines whether the RTS line should be raised or lowered automatically when
transmitting data.
7
8
Set this property to True when using an RS-485 board or converter that uses the RTS line to
enable the transmit line. In this mode, RTS will be raised whenever the program is
transmitting data and lowered at all other times.
" Caution: This property should be set to True only when a program is using RS-485 ports or
9
10
11
12
converters and only if those ports or converters use RTS for line control. Enabling this
property at other times could cause programs to behave erratically or stop working
completely.
Because RS-485 mode requires control over the RTS line, the RTS property is set to False
and CTS/RTS hardware flow control is disabled whenever RS485Mode is set to True.
See “RS-485 support overview” on page 31 for more information on RS-485 support.
See also: BaseAddress
RTS
13
14
property
property RTS : Boolean
Default: True
! Determines the current state of the “request to send” signal (RTS).
15
16
This signal is usually used for hardware flow control, in which case your application does
not need to set it directly. Less frequently, devices require that your application raise and
lower RTS to control the device, or require that RTS be permanently set. Use this property in
those cases.
17
86 Chapter 2: Port Component
1
1
The following example lowers the RTS signal after opening the port and later raises it again:
ApdComPort := TApdComPort.Create(Self);
ApdComPort.Open := True;
ApdComPort.RTS := False;
...
ApdComPort.RTS := True;
1
2
3
See also: DTR, HWFlowOptions
SendBreak
method
4
procedure SendBreak(Ticks : Word; Yield : Boolean);
! Transmits a break signal.
This method transmits a break signal (the transmit line is held in the “marking” state) for
the number of ticks specified by Ticks. A tick is 55 milliseconds.
6
When Yield is True, SendBreak yields control back to Windows while sending the break,
giving other applications and other parts of this application a chance to run. When Yield is
False, SendBreak does not yield.
7
8
SetBreak
method
9
procedure SetBreak(BreakOn : Boolean);
! Raises or lowers the break signal.
This method will begin transmission of the break signal if BreakOn is True; or stops
transmission of the break signal if BreakOn is False. Use this method if you need to transmit
the break signal for an undetermined time period.
10
11
See also: SendBreak
12
13
14
15
16
17
TApdComPort Component 87
1
1
1
2
3
4
SetStatusTrigger
method
procedure SetStatusTrigger(const Handle : Word;
const Value : Word; const Activate : Boolean);
! Activates or deactivates a status trigger.
Status triggers are activated in two steps. The trigger is added using AddStatusTrigger, then
the trigger is activated using SetStatusTrigger. The trigger type is specified when the trigger
is added, and the exact trigger condition is specified when the trigger is activated.
5
Handle is the value that was returned by the call to AddStatusTrigger. The interpretation of
Value varies between the trigger types, as described below. Activate is True to activate the
trigger, False to deactivate it. When Activate is False the Value parameter is ignored.
6
For triggers of type stModem, Value is a bit mask that contains one or more of the following
options:
7
8
9
10
11
12
13
14
15
16
Option
Description
msCTSDelta
Trigger when CTS changes.
msDSRDelta
Trigger when DSR changes.
msRingDelta
Trigger when a ring is detected.
msDCDDelta
Trigger when DCD changes.
For the msCTSDelta, msDSRDelta, and msDCDDelta options SetStatusTrigger saves the
current state of the corresponding modem signals and checks for changes to those signals.
When a change from the original state is detected an OnTriggerStatus event is generated. If a
single trigger is used to monitor multiple signals, the message response routine must check
the appropriate modem status properties to determine which signal actually changed state.
Alternatively, a separate trigger can be added for each modem signal. Once a trigger
message is sent the trigger is disabled, even if some of the monitored signals did not change
state.
The msRingDelta option triggers an OnTriggerStatus event at the end of the next incoming
ring signal, immediately after the audible termination of the ring. In order to receive another
OnTriggerStatus event using msRingDelta, the application must not only call
SetStatusTrigger again, but it must also read the DeltaRI property to clear the ring condition
in the modem status register.
An stModem trigger also generates an OnTriggerModemStatus event. Note, however, that
no trigger handle is passed to the OnTriggerModemStatus event handler, so it cannot
distinguish among multiple different triggers. If you need to do this, use an OnTriggerStatus
event handler instead.
17
88 Chapter 2: Port Component
1
1
For triggers of type stLine, Value is a bit mask that contains one or more of the following
options:
Option
Description
lsOverrun
Trigger on UART overrun errors.
lsParity
Trigger on parity errors.
lsFraming
Trigger on framing errors.
lsBreak
Trigger on a received line break signal.
1
2
3
4
If a single trigger is used to monitor multiple line status signals, the OnTriggerStatus event
handler must read the LineError property to determine the most severe error. When lsBreak
is combined with other options the response routine must read both LineError and
LineBreak to determine whether the trigger was caused by an error or by a received line
break.
6
An stLine trigger also generates an OnTriggerLineError event, which passes the current
values of LineError and LineBreak as parameters to its handler.
7
For triggers of type stOutBuffFree, an OnTriggerStatus event is generated when the number
of bytes free in the output buffer is greater than or equal to Value. An OnTriggerOutbuffFree
event is also generated by the trigger.
For triggers of type stOutBuffUsed, an OnTriggerStatus event is generated when the number
of bytes used in the output buffer is less than or equal to Value. An OnTriggerOutbuffUsed
event is also generated by the trigger.
For triggers of type stOutSent, Value is not used. Here, an OnTriggerStatus event is
generated whenever PutChar, PutString, or PutBlock is called. However, the event is not
generated directly from these routines, but is instead generated the next time the dispatcher
gains control. Only one event is generated even if multiple PutXxx calls were made or the
Output property was assigned since the last time the dispatcher ran.
8
9
10
11
12
All status triggers except stOutSent must be restarted within the message handler. That is,
the triggers generate a single message and do not restart themselves automatically.
13
The following example adds an stOutBuffFree status trigger and activates it to send a
message when at least 100 bytes are free in the output buffer:
14
var
MyHandle : Word;
...
MyHandle := ApdComPort.AddStatusTrigger(stOutBuffFree);
ApdComPort.SetStatusTrigger(MyHandle, 100, True);
15
16
17
TApdComPort Component 89
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
The following example adds an stModem trigger and activates it to send a message when
either the DSR or CTS signal changes from its current state:
var
MyHandle : Word;
...
MyHandle := ApdComPort.AddStatusTrigger(stModem);
ApdComPort.SetStatusTrigger(
MyHandle, msDSRDelta or msCTSDelta, True);
See also: AddStatusTrigger, OnTriggerStatus
SetTimerTrigger
procedure SetTimerTrigger(const Handle : Word;
const Ticks : LongInt; const Activate : Boolean);
! Activates or deactivates a timer trigger.
Timer triggers are activated in two steps. The trigger is added using AddTimerTrigger, then
the trigger is activated using SetTimerTrigger. The duration of the timer is specified when
the trigger is activated.
Handle is the handle returned when the trigger was added. Ticks is the duration of the timer
in BIOS clock ticks (a tick is approximately 55 milliseconds).
Activate is True to activate the trigger, False to deactivate it. When Activate is False the Ticks
parameter is ignored.
After the specified time elapses the internal dispatcher generates an OnTriggerTimer event.
The trigger handle is passed to the event handler so that it can detect which timer expired.
Timer triggers generate a single OnTriggerTimer event. The timer is automatically disabled
after it triggers once. To reuse the timer your program must call SetTimerTrigger again.
The following example adds a timer trigger and activates it with a 36 tick (2 second)
timeout:
var
MyHandle : Word;
...
MyHandle := ApdComPort.AddTimerTrigger;
ApdComPort.SetTimerTrigger(MyHandle, 36, True);
See also: AddTimerTrigger
16
17
90 Chapter 2: Port Component
1
1
method
StopBits
property
1
property StopBits : Word
2
Default: 1
! Determines the number of stop bits of the port.
Acceptable values are 1 and 2. If DataBits equals 5, a request for 2 stop bits is interpreted as a
request for 1.5 stop bits, the standard for this data size.
If the port is open when StopBits is changed, the line parameters are updated immediately.
StopBits does not validate the assigned value before passing it on to the communications
driver. The driver may reject the value, leading to an exception.
3
4
See also: Baud, ComNumber, DataBits, Parity
SWFlowOptions
property
6
7
property SWFlowOptions : TSWFlowOptions
TSWFlowOptions = (swfNone, swfReceive, swfTransmit, swfBoth);
8
Default: swfNone
! Determines the software flow control options for the port.
This routine turns on one or both aspects of automatic software flow control based on the
value assigned to the property.
“Receive flow control” stops a remote device from transmitting while the local receive buffer
is too full. “Transmit flow control” stops the local device from transmitting while the remote
receive buffer is too full.
Receive flow control is enabled by assigning swfReceive or swfBoth to the property. When
enabled, an XOff character is sent when the input buffer reaches the level assigned to the
BufferFull property. The remote must recognize this character and stop sending data after it
is received.
As the application processes received characters, buffer usage eventually drops below the
level assigned to the BufferResume property. At that point, an XOn character is sent. The
remote must recognize this character and start sending data again.
Transmit flow control is enabled by assigning swfTransmit or swfBoth to the property. The
BufferFull and BufferResume properties are not used in this case. When transmit flow
control is enabled, the communications driver stops transmitting whenever it receives an
XOff character. The driver does not start transmitting again until it receives an XOn
character or the application sets SWFlowOptions to swfNone.
9
10
11
12
13
14
15
16
17
TApdComPort Component 91
1
1
1
2
3
4
5
The following example enables bi-directional software flow control with limits at the 25%
and 75% levels of the buffer. The default characters are used for XOn and XOff. Later in the
application, software flow control is disabled.
ApdComPort.BufferFull := Trunc(0.75*ApdComPort.InSize);
ApdComPort.BufferResume := Trunc(0.25*ApdComPort.InSize);
ApdComPort.SWFlowOptions := swfBoth;
...
ApdComPort.SWFlowOptions := swfNone;
See also: FlowState, HWFlowOptions
TapiMode
property
property TapiMode : TTapiMode
6
TTapiMode = (tmNone, tmAuto, tmOn, tmOff);
Default: tmAuto
7
8
9
10
11
12
13
14
15
16
! Determines whether a TApdComPort can be controlled by a TApdTapiDevice.
A TApdTapiDevice cannot work by itself; it must work in conjunction with a
TApdComPort. When a TApdTapiDevice is created, it searches the form for a
TApdComPort. If it finds one, it checks the comport component’s TapiMode property to
determine whether it can be used by the TApdTapiDevice.
If TapiMode is tmAuto (the default), the TApdComPort is available for TAPI use. The
TApdTapiDevice saves a pointer to the TApdComPort and sets these property values:
ApdComPort.TapiMode := tmOn;
ApdComPort.AutoOpen := False;
ApdComPort.Open := False;
TapiMode is changed to tmOn to indicate that the TApdComPort is being controlled by the
associated TApdTapiDevice. AutoOpen and Open are both set to False because the
TApdComPort should no longer control when it is opened or closed—that is now done by
TAPI.
To turn off TAPI mode, or to prevent a TAPI device from taking control of the
TApdComPort, set TapiMode to tmOff. To re-enable TAPI mode later, set TapiMode back to
tmAuto or tmOn. You must also set AutoOpen and Open to False because the
TApdTapiDevice automatically sets these properties only when either the TApdTapiDevice
or TApdComPort are first created.
The value tmNone isn’t used.
17
92 Chapter 2: Port Component
1
1
See the ADXPORT form/unit in the TERMDEMO demonstration program (see the Async
Professional Developer’s Guide) for an example of a program that uses both TAPI devices
and direct serial port access. It modifies TapiMode accordingly as the user selects either
TAPI devices or direct serial ports.
1
2
See “Chapter 8: TAPI Components” on page 203 for more information on TAPI.
3
See also: AutoOpen, Open
TraceAllHex
property
4
property TraceAllHex : Boolean
Default: False
! Determines when the trace log will contain literal printable characters, or if all characters
will be written in hexadecimal notation.
6
See also: LogAllHex, Tracing
7
TraceHex
property
8
property TraceHex : Boolean
Default: True
! Determines whether non-printable characters stored in a trace file are written using
9
hexadecimal or decimal notation.
10
See also: TraceAllHex, TraceName, TraceSize, Tracing
TraceName
property
11
property TraceName : ShortString
12
Default: APRO.TRC
! Determines the name of the file used to store a trace.
13
See also: Tracing
14
15
16
17
TApdComPort Component 93
1
1
1
TraceSize
property
property TraceSize : Word
2
Default: 10000
! Determines the number of entries allocated in the trace buffer.
3
4
5
The value may be as large as 4 million. Each entry consumes 2 bytes.
This property should normally be set before a tracing session begins. If a changed value is
assigned to TraceSize while a tracing session is active, the current session is aborted (which
clears all information from the trace buffer), the new buffer is allocated, and a new trace
session is started.
See also: Tracing
6
7
Tracing
property
property Tracing : TTraceLogState
TTraceLogState = (tlOff, tlOn, tlDump, tlAppend, tlClear, tlPause);
8
9
10
11
12
13
14
Default: tlOff
! Determines the current tracing state.
When Tracing is set to tlOff, as it is by default, no tracing is performed.
To enable tracing, set Tracing to tlOn. This allocates an internal buffer of 2*TraceSize bytes
and informs the dispatcher to start using this buffer. To disable tracing without writing the
contents of the buffer to a disk file, set Tracing to tlOff. This also frees the internal buffer.
To write the contents of the tracing buffer to disk, set Tracing to tlDump (which overwrites
any existing file named TraceName, or creates a new file) or tlAppend (which appends to an
existing file, or creates a new file). After the component writes to the file it sets Tracing to
tlOff.
Note that Tracing is usually not as useful as the dispatcher log. The trace file will contain
groupings of transmitted and received characters, which may not be in chronological order.
Dispatcher logging will also show most internal state machine states, which tracing does not
provide.
15
To clear the contents of the tracing buffer and continue tracing, set Tracing to tlClear. After
the component clears the buffer, it sets Tracing to tlOn.
16
To temporarily pause tracing, set Tracing to tlPause. To resume, set Tracing to tlOn.
See “Tracing” on page 33 for more information.
17
94 Chapter 2: Port Component
1
1
The following example turns on tracing and later dumps the tracing buffer to APRO.TRC:
ApdComPort.Tracing := tlOn;
...
ApdComPort.TraceName := 'APRO.TRC';
ApdComPort.Tracing := tlDump;
2
See also: Logging, TraceHex, TraceName, TraceSize
UseEventWord
1
3
property
4
property UseEventWord : Boolean
Default: True
! Determines how the dispatcher checks for received data.
The Windows communication API provides two methods to check for received data and
line/modem status changes: API calls and an event word. The event word is maintained by
the Windows communications driver. As data is received or line/modem status changes
occur, the driver sets bits in the event word. The application can check the bits to determine
if any communication events occurred. If so, the application can make the appropriate API
call to clear the event word and retrieve the data or the new line/modem status values.
Windows also provides API calls to retrieve the same status information provided by the
event word but the API calls are slower. Async Professional uses the event word by default
for the fastest possible performance. Unfortunately, there is at least one communication
driver (WRPI.DRV, included with some U.S. Robotics modems) that doesn’t appear to
support the event word. For this and similar drivers, UseEventWord must be set to False
before Async Professional will receive data.
" Caution: Yielding introduces the possibility of reentrancy, which your application must
anticipate and prevent. For example, if WaitForString is called from within a button’s
OnClick event handler with Yield set to True, the user is able to navigate back to the button
and click on it again. Although WaitForString would work in this situation (and would
thereby start a wait within a wait) you probably do not want this to happen. It’s up to the
application to prevent this by disabling the button or the screen containing the button or by
checking for reentrancy within the OnClick event handler.
The reentrancy issue also applies to other parts of the application since most applications
provide menu options and dialog boxes for changing port parameters, starting file transfers,
dialing the modem, and so on. The application must prevent the user from instigating any
actions that interfere with WaitForString until WaitForString returns.
6
7
8
9
10
11
12
13
14
15
16
17
TApdComPort Component 95
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
None of these problems apply when Yield is False because WaitForString won’t allow other
message processing while it is waiting. However, you should use this approach only for brief
periods of just a few ticks since it prevents Windows from processing other applications and,
worse yet, worries your user since the machine appears frozen until WaitForString returns.
Note that WaitForString uses GetChar to retrieve data, which may prevent this data from
being seen by any trigger handlers for the same comport component (unless WaitForString
is called from within a trigger handler itself). However, each received character generates an
OnWaitChar event, so an event handler can be implemented to pass the data to other
processes.
Note that WaitForString is depreciated and maintained for backward compatibility. In most
cases, other alternatives, such as using the TApdDataPacket component or data triggers, are
more appropriate for Windows applications. Data triggers avoid the reentrancy problems
while still allowing Windows to process messages for other applications and other windows
in the current application. Data triggers are more complex to use than WaitForString but are
well worth the effort in the long run.
The following example shows an OnWaitChar event handler that manually stuffs received
data into a terminal window:
procedure TForm.ApdComPort1WaitChar(CP : TObject; C : Char);
begin
ApdTerminal1.StuffChar(C);
ApdTerminal1.ForcePaint;
end;
The following example is the OnClick event handler from a “Login” button that waits for
and responds to “login” and “password” prompts from a remote host:
procedure TForm1.LoginClick(Sender : TObject);
begin
ApdComPort.Output := 'ATDT260-9726'^M;
if not ApdComPort.WaitForString('login', 1092, True, True) then
...handle timeout error
ApdComPort.Output := 'myname';
if not ApdComPort.WaitForString(
'password', 182, True, True) then
...handle timeout error
ApdComPort.Output := 'secret';
...
end;
See also: AddDataTrigger, OnTriggerData, OnWaitChar, WaitForMultiString
17
96 Chapter 2: Port Component
1
1
XOffChar
property
1
property XOffChar : Char
2
Default: #19 (^S)
! Determines the character that is sent to disable remote sending when software flow control is
active.
3
Software flow control almost universally uses the XOff (ASCII 19) character to suspend
transmission, and this is the default character used by Async Professional. If you should
encounter a device that requires a different character, you can use XOffChar to set it.
4
See also: SWFlowOptions, XOnChar
XOnChar
property
6
property XOnChar : Char
Default: DefXOnChar (#17, ^Q)
7
! Determines the character that is sent to enable remote sending when software flow control is
active.
8
Software flow control almost universally uses the XOn (ASCII 17) character to enable
transmission, and this is the default character used by Async Professional. If you should
encounter a device that requires a different character, you can use XOnChar to set it.
9
10
11
12
13
14
15
16
17
TApdComPort Component 97
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
98 Chapter 2: Port Component
1
1
Chapter 3: Winsock Components
1
2
Windows includes routines for network and Internet communications. These routines are
contained in DLLs which are collectively called Winsock (for WINdows SOCKets). Winsock
is a Windows-specific implementation of the Berkley Sockets API. The Berkley Sockets API
was developed as a protocol to allow UNIX machines to communicate with each other over
networks. The concept of sockets is analogous to a telephone operator in the early days of
telephones. When a call came in, the operator used a patch cord to connect the caller’s
socket to the socket of the person being called. Winsock does essentially the same thing. It
provides a means of connecting a calling computer to a host computer so that the two can
exchange information. The calling application is called a client and the host application is
called a server.
3
4
5
6
Before a connection can be established, Winsock needs to know how to find the host
computer. Each network computer has an address associated with it. This address, called the
IP address, is a 32-bit value that uniquely identifies the machine. Since a number like
32,147,265 is difficult to remember, network addresses are often displayed in dot notation.
Dot notation specifies an IP address as a series of four bytes, each separated by a dot. For
example, the TurboPower Web site address can be specified in dot notation as
165.212.210.12. Network software translates the address specified in dot notation to a real
32-bit value.
7
8
9
" Caution: Leading zeros in a dot notation IP address (for example, “198.168.010.012”)
10
causes Winsock to interpret the respective portion of the address in octal (the above IP
would actually be interpreted by Winsock as “198.168.8.10”). APRO does not interfere with
this behavior, it simply passes the entered address to Winsock as is.
11
While expressing a network address in dot notation is a little better than dealing with a raw
32-bit value, it is still not particularly easy to remember. For that reason, a global database
gives you the capability to specify an IP address in plain text. This database, called the
Domain Name Service (DNS), has text entries that correspond to IP address values. For
example, the TurboPower Web site DNS entry is www.turbopower.com. If Winsock does a
lookup for the host name www.turbopower.com, it gets the IP address 165.212.210.12.
12
13
Not all computers have DNS entries. A DNS entry is usually used to provide public access to
a computer. Servers that are for private use only don’t publish their IP addresses.
14
Most software allows you to specify either the host name or the IP address in dot notation
when attempting to connect to a server. To illustrate, start your favorite Web browser and
type “www.turbopower.com” at the address prompt. When you hit Enter, your browser
displays the home page of the TurboPower Web site. Now try again, but this time type
“165.212.210.12” at the address prompt. Once again the browser takes you to the
TurboPower Web site.
15
16
17
99
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
In addition to IP addresses, Winsock uses ports to specify how to connect to a remote
machine. Winsock can be thought of as a trunk line with thousands of individual lines (the
ports) which are used to connect machines. Some ports are considered well-known ports.
For example, the port typically used for network mail systems (SMTP) is port 25, the telnet
port is port 23, the network news server port (NNTP) is typically port 119, and so on. To see
a list of well-known ports, inspect the SERVICES file in the Windows directory (for
Windows NT it is in the WINNT\SYSTEM32\DRIVERS\ETC directory). The SERVICES
file is a text file used by Winsock to perform port lookups (which return the service name
for the specified port) and port name lookups (which return the port number for the
specified service name). You can open this file in any text editor to see a list of port numbers
and their corresponding service names. While these well-known ports are not set in stone,
they are traditional and their use should be reserved for the service which they represent.
When writing network applications, you should select a port number that is not likely to be
duplicated by other applications on your network. In most cases you can choose a port
number other than any of the well-known port numbers.
The IP address and port number are used in combination to create a socket. A socket is first
created and then is used to establish connection between two computers. How the socket is
used depends on whether the application is a client or a server. If an application is a server, it
creates the socket, opens it, and then listens on that socket for computers trying to establish
a connection. At this point the server is in a polling loop listening and waiting for a possible
connection. A client application, on the other hand, creates a socket using the IP address of a
particular server and the port number that the server is known to be listening on. The client
then uses the socket to attempt to connect to the server. When the server hears the
connection attempt, it wakes up and decides whether or not to accept the connection.
Usually this is done by examining the IP address of the client and comparing it to a list of
known IP addresses (some servers don’t discriminate and accept all connections). If the
connection is accepted, the client and server begin communicating and data is transmitted.
There is one other aspect of Internet communications that should be noted. Telnet is a
protocol that allows a computer to connect to a remote server via a terminal screen. When a
connection is established, a telnet server sends ASCII data to the client application. The
client application then displays the text on the terminal screen. Telnet applications typically
use port 23.
The telnet protocol describes option negotiation (typically at the beginning of a session)
and escaping of certain characters during the entire communication session. This
processing is enabled by the WsTelnet property (which is True by default). If the client or
server you are communicating with does not support telnet processing, you should set
WsTelnet to False prior to opening the port.
Note: If WsTelnet is True, and the client or server to which you are connecting does not
support telnet processing, it may appear that your data is being corrupted because telnet
processing modifies the data stream.
100 Chapter 3: Winsock Components
1
1
Sockets in Async Professional
1
Async Professional includes a device layer, dlWnsock, that utilizes Winsock for network and
Internet communications.
The Async Professional implementation of Winsock consists of two components.
TApdWinsockPort is a component that replaces the TApdComPort component and can be
placed on a form at design time. TApdWinsockPort includes properties to allow you to set
the network address, the port number, and the mode of the socket (server mode or client
mode).
TApdWinsockPort is derived from TApdCustomComPort and therefore inherits all of its
properties and methods. Many of these properties and methods are not applicable to
TApdWinsockPort when operating in Winsock mode, but are retained in the descendent
component so that TApdWinsockPort behaves exactly like TApdComPort when the
DeviceLayer property is not set to dlWinsock. The properties and methods that do not
apply to Winsock operation (e.g., Baud, DataBits, Parity, and StopBits) are simply ignored
when DeviceLayer is set to dlWinsock. Certain Async Professional components (e.g., the
modem and TAPI components) are not applicable when using the dlWinsock device layer.
Faxing over the Internet is not supported because Internet faxing uses a different protocol
than faxmodems.
TApdSocket is a low-level component that provides access to most standard Winsock
services. This component is used internally by Async Professional. A global instance of this
component, ApdSocket, is created for use by the Winsock device layer in the initialization
code of the AwWnsock unit. In most cases you won’t need to, but you can create your own
instance of this class.
The Winsock support in Async Professional is not intended as a full-featured Winsock
implementation. Rather, it is intended to allow you to perform basic communications
operations over local networks or over the Internet. Certain concessions were made (such as
allowing only one client connection to a server socket) to allow the Winsock
implementation to fit into the existing Async Professional communications model.
2
3
4
6
7
8
9
10
11
12
13
Winsock NIC selection
Some systems are configured with multiple IP addresses, perhaps a physical network card
for local intranet access and a RAS network for Internet access. Some peripherals (i.e., IR
ports under Windows 2000) install themselves as a separate network. The
WsLocalAddresses and WsLocalAddressIndex property allow the developer to select which
network to use.
14
15
16
17
Chapter 3: Winsock Components 101
1
1
1
2
Winsock proxy/firewall support
Some systems must go through a firewall/proxy to access remote systems. In this case, the
TApdSocksServerInfo class (via the WsSocksServerInfo property) is used to specify the
proxy server to connect through.
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
102 Chapter 3: Winsock Components
1
1
TApdSocksServerInfo Class
1
TApdSocksServerInformation contains the location of the proxy server. If a proxy is to use
be used with the TApdCustomWinsockPort, these values must be properly configured
before opening the connection. When a connection attempt is started (Open property set to
true), the TApdCustomWinsockPort will connect to the proxy server and negotiate the
connection to the address specified by the WsAddress and WsPort properties. When the
connection to the destination is made, the OnWsConnect event will be generated.
2
If the connection attempt fails, the OnWsError event is generated and the Open property is
set to False. The developer can test the ErrorCode parameter of the OnWsError event to
determine whether the failure was due to the SOCKS server or due to some other type of
failure.
3
4
6
Hierarchy
7
TPersistent (VCL)
TApdSocksServerInfo (AdWnPort)
8
Properties
Address
SocksVersion
Password
Port
UserCode
9
10
11
12
13
14
15
16
17
TApdSocksServerInfo Class 103
1
1
1
Reference Section
Address
run-time property
2
property Address : string
3
! Specifies the address of the proxy server.
Password
run-time property
4
property Password : string
5
! Specifies the password needed to access the proxy server.
Port
run-time property
6
property Port : Word
7
! Specifies the port number of the proxy server.
SocksVersion
run-time property
8
property SocksVersion : TApdSocksVersion
9
TApdSocksVersion = (svNone, svSocks4, svSocks5);
! Specifies the version of the proxy server.
10
11
The SocksVersion property can be set to the following values:
Value
Meaning
svNone
No proxy support is required. Opening the port will
establish a connection by using the WsAddress and WsPort
properties directly.
svSocks4
Specifies Socks4.
svSocks5
Specifies Sock5.
12
13
14
15
Socks4a has extended Socks4 by adding DNS lookup by the server. Both Socks4 and
Socks4a are supported. If SocksVersion is set to svSocks4, Socks4 will be attempted if the
WsAddress property contains a dotted-quad IP address, or Socks4a will be used if the
WsAddress property contains a domain name.
16
17
104 Chapter 3: Winsock Components
1
1
More information on Socks4 can be found on
http://www.socks.nec.com/protocol/socks4.protocol and
http://www.socks.nec.com/protocol/socks4a.protocol
1
More information on Socks5 can be found on http://www.eborder.nec.com/index2.htm,
http://www.socks.nec.com/rfc/rfc1928.txt and http://www.socks.nec.com/rfc/rfc1929.txt.
UserCode
run-time property
property UserCode : string
2
3
4
! Specifies the user name or code needed to access the proxy server.
6
7
8
9
10
11
12
13
14
15
16
17
TApdSocksServerInfo Class 105
1
1
1
TApdWinsockPort Component
2
The TApdWinsockPort component provides a Winsock port that can be used to establish a
TCP/IP connection. In addition, it provides all of the services of the standard
TApdComPort component. For a description of the properties, events, and methods of
TApdComPort, see “TApdComPort Component” on page 22. To put the TApdWinsockPort
in Winsock mode, simply set the DeviceLayer property to dlWinsock.
3
4
5
6
7
8
If you use the TApdWinsockPort in Winsock mode, it cannot be used with the TAdModem
because in this case Async Professional is not directly controlling a modem. It also cannot be
used with TApdSendFax or TApdReceiveFax because in this case Async Professional is not
directly communicating with a faxmodem or fax machine.
The TApdWinsockPort component is an implementation of the Winsock version 1.1 API.
Example
This example shows how to connect to the Library of Congress via telnet. Create a new
project, add the following components, and set the property values as indicated in Table 3.1.
Table 3.1: Example components and property values
9
Component
Property
Value
TApdWinsockPort
DeviceLayer
dlWinsock
10
11
15
16
80
25
Caption
Open
TButton
Caption
Close
Double-click on the Open button’s OnClick event handler in the Object Inspector and
modify the generated method to match this:
procedure TForm1.OpenClick(Sender : TObject);
begin
ApdWinsockPort1.Open := True;
end;
106 Chapter 3: Winsock Components
1
Columns
Rows
17
1
False
TButton
12
14
locis.loc.gov
AutoOpen
TAdEmulator
TAdTerminal
13
WsAddress
Double-click on the Close button’s OnClick event handler in the Object Inspector and
modify the generated method to match the following code:
procedure TForm1.CloseClick(Sender : TObject);
begin
ApdWinsockPort1.Open := False;
end;
1
2
Establish a connection to the Internet (e.g., using Windows Dialup Networking).
Compile and run the example. Of course, this is a bare-bones application—but it
demonstrates the potential of the TApdWinsockPort.
3
4
Hierarchy
TComponent (VCL)
6
! TApdBaseComponent (OOMisc) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
" TApdComPort (AdPort) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
7
TApdWinsockPort (AdWnPort)
8
Properties
" AutoOpen
" DSR
Open
" BaseAddress
" DTR
" OutBuffFree
" Baud
" FlowState
" OutBuffUsed
" BufferFull
" HWFlowOptions
" Output
" BufferResume
" InBuffFree
" OutSize
" ComHandle
" InBuffUsed
" Parity
" ComNumber
" InSize
" RI
" CTS
" LineBreak
" RS485Mode
" DataBits
" LineError
" RTS
" DCD
" LogAllHex
" StopBits
" DeltaCTS
" Logging
" SWFlowOptions
" DeltaDCD
" LogHex
" TapiMode
" DeltaDSR
" LogName
" TraceAllHex
" DeltaRI
" LogSize
" TraceHex
" ModemStatus
" TraceName
DeviceLayer
9
10
11
12
13
14
15
16
17
TApdWinsockPort Component 107
1
1
1
2
" TraceSize
WsLocalAddresses
" Tracing
WsLocalAddressIndex
" XOffChar
" UseEventWord
WsMode
" XOnChar
! Version
WsPort
WsAddress
3
4
5
6
7
8
9
10
11
" ActivateDeviceLayer
" ForcePortOpen
" PutString
" AddDataTrigger
" GetBlock
" RemoveAllTriggers
" AddStatusTrigger
" GetChar
" RemoveTrigger
" AddTimerTrigger
" InitPort
" SendBreak
" AddTraceEntry
" PeekBlock
" SetBreak
" CharReady
" PeekChar
" SetStatusTrigger
" CheckForString
" ProcessCommunications
" SetTimerTrigger
" FlushInBuffer
" PutBlock
" FlushOutBuffer
" PutChar
Events
" OnPortClose
" OnTriggerModemStatus
OnWsAccept
" OnPortOpen
" OnTriggerOutbuffFree
OnWsConnect
" OnTrigger
" OnTriggerOutbuffUsed
OnWsDisconnect
" OnTriggerAvail
" OnTriggerOutSent
OnWsError
" OnTriggerData
" OnTriggerStatus
" OnTriggerLineError
" OnTriggerTimer
13
14
15
16
17
108 Chapter 3: Winsock Components
1
WsSocksServerInfo
Methods
12
1
WsTelnet
Reference Section
1
DeviceLayer
property
2
property DeviceLayer : TDeviceLayer
TDeviceLayer = (dlWin16, dlFossil, dlWin32, dlWinsock);
3
Default: dlWinsock
4
! Determines the hardware interface used by the port.
The DeviceLayer property determines whether the TApdWinsockPort is acting as a
Winsock port (dlWinsock) or as a serial port (dlWin32). Since the TApdWinsockPort is a
descendent of the TApdCustomComPort, this component can be used almost
interchangeably with the TApdComPort component. To switch between Winsock and serial
ports, change the DeviceLayer property. DeviceLayer must be dlWinsock to connect via
Winsock.
7
You can create custom device layers by deriving them from TApdBaseDispatcher and
creating a new port descendant from TApdCustomComport where you override
ActivateDeviceLayer to return the newly defined device layer.
OnWsAccept
6
8
event
9
property OnWsAccept : TWsAcceptEvent
TWsAcceptEvent = procedure (
Sender : TObject; Addr : TInAddr; var Accept : Boolean) of object;
! Defines an event handler that is called when a client attempts to connect to a server.
This event is generated when an application is acting as a server (WsMode equals WsServer)
and a client application attempts a connection. Addr is the network address of the client. To
accept the connection, set Accept to True. To refuse the connection, set Accept to False.
OnWsAccept is not generated when an application is acting as a client.
10
11
12
13
14
15
16
17
TApdWinsockPort Component 109
1
1
1
2
3
4
5
6
The following example calls a user-supplied function named GoodAddress to determine
whether the network address is one for which connection will be accepted. If GoodAddress
returns True, Accept is set to True and the connection is made. If GoodAddress returns
False, Accept is set to False and the connection is refused.
procedure TForm1.WsPortWsAccept(
Sender : TObject; Addr : TInAddr; var Accept : Boolean);
begin
if GoodAddress(Addr) then begin
Status.Caption := 'Accepted!';
Accept := True;
end else begin
Status.Caption := 'Connection Denied';
Accept := False;
end;
end;
See also: OnWsConnect, WsMode
7
8
OnWsConnect
event
property OnWsConnect : TNotifyEvent
! Defines an event handler that is called when a Winsock connection is established.
9
10
When an application is operating as a client (WsMode equals WsClient,) it usually attempts
to connect to a server. This event is generated when the server accepts the connection. This
event is not generated when an application is acting as a server.
11
The following example illustrates a client application receiving notification that a
connection to the server was established and accepted by the server:
12
13
procedure TForm1.WsPortWsConnect(Sender : TObject);
begin
Status.Caption := 'Connected';
{ do some processing… }
end;
See also: OnWsAccept, OnWsDisconnect, WsMode
14
15
16
17
110 Chapter 3: Winsock Components
1
1
OnWsDisconnect
event
1
property OnWsDisconnect : TNotifyEvent
! Defines an event handler that is called when a Winsock connection is dropped.
2
A connection can be dropped as the result of an error or when a transmission is complete
and one end terminates the connection.
If WsMode equals WsServer, OnDisconnect is generated when the client is disconnected.
The Open property stays True and the TApdWinsockPort continues to listen for other
clients attempting to connect. If WsMode equals WsClient, OnDisconnect is generated
when the connection is lost. Async Professional then sets the Open property to False.
The following example illustrates a server application receiving notification that the client
has disconnected:
procedure TForm1.WsPortWsDisconnect(Sender : TObject);
begin
Status.Caption := 'Bye!';
end;
3
4
6
7
See also: OnWsConnect, Open, WsMode
8
OnWsError
event
9
property OnWsError : TWsErrorEvent
TWsErrorEvent = procedure(
Sender : TObject; ErrorCode : Integer) of object;
! Defines an event handler that is generated when a Winsock error occurs.
ErrorCode contains the Winsock error code. See “Error Handling and Exception Classes”
on page 900 for a list of error codes.
10
11
12
13
14
15
16
17
TApdWinsockPort Component 111
1
1
1
Open
property
property Open : Boolean
2
Default: False
! Determines whether the Winsock port is open and initialized.
3
4
5
6
7
8
9
10
11
12
13
Open must be set to True before a Winsock port can send or receive characters. When Open
is set to True, the TApdWinsockPort uses all current property settings to allocate input and
output buffers, create a socket, open the Winsock port, and enable or disable tracing and
logging. It then registers a low-level trigger handler, which gets the first look at all trigger
events and passes control on to the appropriate OnTriggerXxx event handlers.
When Open is set to False, the TApdWinsockPort sets the tracing and logging properties to
tlDump (which creates output files if any information was buffered), closes the Winsock
port, and deallocates input and output buffers.
There is no harm done by setting Open to True when it is already True, or setting it to False
when it is already False. If WsMode equals WsServer and you set Open to True, a socket is
created and it listens at the port designated by the WsPort property. If WsMode equals
WsClient and you set Open to True, the component attempts to connect to a server at the
designated WsAddress and WsPort.
WsAddress
property WsAddress : string
! The network address used to make a Winsock connection.
WsAddress accepts the IP address in dot notation (165.212.210.10) or as a host name
(telnet.turbopower.com). If a host name is used, Async Professional does a DNS lookup to
determine whether a DNS entry exists for the host name. If an IP address can be found, the
port is opened. If an IP address cannot be found, a EApdSocketException is raised.
" Caution: Do not add leading zeros in dot notation addresses (e.g., 165.212.210.010).
Leading zeros will cause the number to be interpreted as an octal value.
14
15
16
17
112 Chapter 3: Winsock Components
1
1
property
WsLocalAddresses
read-only, run-time property
1
property WsLocalAddresses : TStringList
! Lists the IP addresses for each network interface that is installed.
2
WsLocalAddresses is a read-only TStringList containing the IP address for each network
interface installed on the computer. This is property is populated when the
TApdCustomWinsockPort is created. The network interface to use is specified by the
WsLocalAddressIndex property.
3
4
See also: WsLocalAddressIndex
WsLocalAddressIndex
run-time property
property WsLocalAddressIndex : Integer
6
! Determines the network interface to use.
To select a network intefacee, set WsLocalAddressIndex to the index of the network
interface listed in the WsLocalAddresses property.
7
See also: WsLocalAddresses
8
WsMode
property
9
property WsMode : TWsMode
TWsMode = (wsClient, wsServer);
10
Default: WsClient
! Determines whether the application operates as a server or a client.
If WsMode is WsServer, the application acts as a server. When the Open property is set to
True, the application begins listening for possible connections on the port specified by
WsPort.
If WsMode is WsClient, the application operates as a client. When the Open property is set
to True, the client attempts to connect to the server at the address specified by WsAddress.
When Open is set to False, the client disconnects from the server and the socket is closed.
11
12
13
14
See also: Open, WsAddress, WsMode, WsPort
15
16
17
TApdWinsockPort Component 113
1
1
1
WsPort
property
property WsPort : string
2
Default: “telnet”
! The Winsock port used to establish a network connection.
3
4
5
6
WsPort is the Winsock port on which to connect (for a client application) or on which to
listen (for a server application). WsPort accepts the port as an integer or a service name
(e.g., telnet). If a service name is used, Winsock performs a lookup when the port is opened
to match the service name with a port number. For a list of service names and their
corresponding port numbers, see the SERVICES file in the Windows directory (for
Windows NT it is in the WINNT\SYSTEM32\DRIVERS\ETC directory.
See also: WsAddress
WsSocksServerInfo
7
8
9
10
run-time property
property WsSocksServerInfo : TApdSocksServerInfo
! Contains the Firewall/Proxy configuration.
WsSocksServerInfo contains the configuration of the proxy server and the type of proxy
server in use. If a proxy server is in use, this must be properly configured before opening the
port.
WsTelnet
property
property WsTelnet : Boolean
11
12
13
14
Default: True
! Indicates whether telnet processing is enabled.
For most uses of the TApdWinsockPort (such as connecting to telnet servers or
communication between TApdWinsockPort components) the default value of True is
appropriate. However, if you communicate with a server or client that does not support
telnet processing you should set WsTelnet to False.
WsTelnet cannot be changed while the port is open. The value of WsTelnet when the port is
opened is used by the device layer for the duration of that communication session.
15
16
17
114 Chapter 3: Winsock Components
1
1
TApdSocket Component
1
The TApdSocket component is a low-level class that provides many standard Winsock
services. It is essentially a thin wrapper around the Winsock API and transparently handles
tasks such as loading, starting, and shutting down Winsock. It is used internally by the
Async Professional Winsock device layer and the TApdWinsockPort component. In most
cases you won’t need to make use of the TApdSocket component directly, but it is
documented here in case you do.
2
A global instance of the TApdSocket class (ApdSocket) is created in the initialization code of
the Winsock device layer unit (AwWnsock), and is available for use in your application. To
use its services, simply add AwWnsock to your unit’s uses clause. Because it is a low-level
class, the Winsock services it provides access to are not documented in detail here. There are
many Winsock references available to consult. The following were useful in the development
of TApdSocket:
• Microsoft Winsock API help file
3
4
6
7
• Microsoft Developer Network CD
• Dumas, Programming Winsock, Sam’s Publishing, ISBN 0-672-30594-1
• Quinn and Shute, Windows Sockets Network Programming, Addison-Wesley,
8
9
ISBN 0-201-63372-8
• Roberts, Developing for the Internet with Winsock, Coriolis Group Books,
10
ISBN 1-883577-42-X
• Chapman, Building Internet Applications with Delphi 2, Que, ISBN 0-7897-0732-2
11
12
13
14
15
16
17
TApdSocket Component 115
1
1
1
Hierarchy
TComponent (VCL)
2
! TApdBaseComponent (OOMisc) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
TApdSocket (AdSocket)
3
4
5
Properties
Description
LocalAddress
Handle
LocalHost
HighVersion
MaxSockets
LastError
SystemStatus
! Version
WSVersion
6
Methods
7
8
9
10
11
12
AcceptSocket
htons
ntohl
BindSocket
ListenSocket
ntohs
CheckLoaded
LookupAddress
ReadSocket
CloseSocket
LookupName
SetAsyncStyles
ConnectSocket
LookupPort
String2NetAddr
CreateSocket
LookupService
WriteSocket
htonl
NetAddr2String
Events
OnWsAccept
OnWsDisconnect
OnWsRead
OnWsConnect
OnWsError
OnWsWrite
13
14
15
16
17
116 Chapter 3: Winsock Components
1
1
Reference Section
1
AcceptSocket
method
2
function AcceptSocket(
Socket : TSocket; var Address : TSockAddrIn) : TSocket;
TSockAddrIn = packed
case Integer of
0: (sin_family :
sin_port
:
sin_addr
:
sin_zero
:
1: (sa_family :
sa_data
:
end;
3
record
4
Word;
Word;
TInAddr;
array[0..7] of AnsiChar);
Word;
array[0..13] of AnsiChar)
6
! Accepts a client that is trying to attach to a listening socket.
Socket is the handle of the socket. Address is a structure that contains information used by
Winsock.
See also: ListenSocket
BindSocket
method
function BindSocket(
Socket : TSocket; Address : TSockAddrIn) : Integer;
TSockAddrIn = packed
case Integer of
0: (sin_family :
sin_port
:
sin_addr
:
sin_zero
:
1: (sa_family :
sa_data
:
end;
7
8
9
10
record
11
Word;
Word;
TInAddr;
array[0..7] of AnsiChar);
Word;
array[0..13] of AnsiChar)
12
13
14
! Associates a local network address and port number with a socket.
Socket is the handle of the socket. Address is a structure that contains information used by
Winsock. Typically a server application calls BindSocket with a specified address and port
number prior to calling ListenSocket. A client application does not typically bind to a
specific address and port.
15
16
See also: CreateSocket, ListenSocket
17
TApdSocket Component 117
1
1
1
CheckLoaded
method
procedure CheckLoaded;
2
! Determines whether the Winsock DLL is loaded and initialized.
3
Call CheckLoaded to see if Winsock is ready for use. If Winsock is not initialized,
CheckLoaded raises an EApdSocketException. This exception can be caught in your
application and responded to accordingly.
4
CloseSocket
5
function CloseSocket(Socket : TSocket) : Integer;
method
! Closes a socket.
6
CloseSocket closes a socket and frees the memory allocated for it. Socket is the handle of the
socket to close.
7
See also: CreateSocket
8
9
10
11
12
13
14
ConnectSocket
method
function ConnectSocket(
Socket : TSocket; Address : TSockAddrIn) : Integer;
TSockAddrIn = packed
case Integer of
0: (sin_family :
sin_port
:
sin_addr
:
sin_zero
:
1: (sa_family :
sa_data
:
end;
record
Word;
Word;
TInAddr;
array[0..7] of AnsiChar);
Word;
array[0..13] of AnsiChar)
! Establishes a network connection.
ConnectSocket is used by a client to connect the socket specified by Socket to a remote host.
Address is a structure that contains information used by Winsock to find the remote host.
See also: BindSocket, CreateSocket
15
16
17
118 Chapter 3: Winsock Components
1
1
CreateSocket
method
1
function CreateSocket : TSocket;
! Creates a socket.
2
If the socket is created successfully, CreateSocket returns a unique socket descriptor that is
used to refer to this socket in subsequent Winsock operations. If the socket cannot be
created, CreateSocket raises an EApdSocketException.
3
See also: BindSocket, CloseSocket
4
Description
read-only, run-time property
property Description : string
! Contains a string that describes the Winsock DLL.
6
Description is a read-only property that contains a textual description of the current
Winsock DLL. Since there are so many different Winsock DLLs, the string returned depends
on the Winsock vendor. For the standard Windows NT 4.0/2000 Winsock DLL, the string is
“Winsock 2.0.” For the standard Windows 95/98/ME Winsock DLL, the string is “Microsoft
Windows Sockets version 1.1.”
See also: HighVersion, SystemStatus, Version
Handle
8
9
read-only, run-time property
10
property Handle : HWnd
! The window handle for the TApdSocket class.
Winsock uses the window handle to send messages to the TApdSocket object. The Winsock
messages received by the object generate the OnWsAccept, OnWsConnect,
OnWsDisconnect, OnWsError, OnWsRead, and OnWsWrite events.
HighVersion
7
read-only, run-time property
11
12
13
property HighVersion : Word
! Contains the highest version of the Winsock specification supported by the current
14
Winsock DLL.
For the Windows NT 4.0 and 2000 Winsock, HighVersion is 2.2, which indicates that it can
support version 2.2 of the Winsock specification. For the Windows 95/98/ME Winsock,
HighVersion is 1.1.
15
16
See also: WSVersion
17
TApdSocket Component 119
1
1
1
htonl
method
function htonl(HostLong : LongInt) : LongInt;
2
3
4
! Translates a 32-bit value from host byte order to network byte order.
IBM-compatible computers typically store data in memory in little-endian byte order (the
least significant byte stored first followed by the most significant byte) or host byte order.
TCP/IP stipulates that data should be sent in big-endian byte order (most significant byte
followed by least significant byte) or network byte order.
See also: htons, ntohl, ntohs
5
6
7
8
9
htons
function htons(HostShort : Word) : Word;
! Translates a 16-bit value from host byte order to network byte order.
IBM-compatible computers typically store data in memory in little-endian byte order (the
least significant byte stored first followed by the most significant byte) or host byte order.
TCP/IP stipulates that data should be sent in big-endian byte order (most significant byte
followed by least significant byte) or network byte order.
See also: htonl, ntohl, ntohs
LastError
10
11
12
! Contains the error code of the last Winsock error.
If a Winsock operation fails, you can use LastError to get the Winsock error code. See “Error
Handling and Exception Classes” on page 900 for a list of the error codes.
See also: OnWsError
14
15
16
17
120 Chapter 3: Winsock Components
1
read-only, run-time property
property LastError : Integer;
13
1
method
ListenSocket
method
function ListenSocket(
Socket : TSocket; Backlog : Integer) : Integer;
1
2
! Tells a socket to listen for a connection attempt.
ListenSocket is used by a server application to enter listening mode. Socket is the socket on
which to listen. Backlog is the maximum length of the queue for waiting connection
attempts. If ListenSocket is successful, 0 is returned. If ListenSocket is not successful, it raises
the EApdSocketException.
3
4
See also: BindSocket, CreateSocket
LocalAddress
read-only, run-time property
6
property LocalAddress : string
! Contains the local machine’s network address.
LocalAddress contains a text string of the local machine’s network address in dot notation
(e.g., “165.212.210.12”).
See also: LocalHost
LocalHost
read-only, run-time property
property LocalHost : string
7
8
9
10
! Contains the local machine’s network name.
LocalHost contains a textual description of the local machine’s network name
(e.g., “garyf-testmachine”).
11
See also: LocalAddress
12
13
14
15
16
17
TApdSocket Component 121
1
1
1
LookupAddress
method
function LookupAddress(InAddr : TInAddr) : string;
2
3
4
5
6
7
8
9
TInAddr = packed record
case Integer of
0 : (S_un_b : SunB);
1 : (S_un_w : SunW);
2 : (S_addr : LongInt);
end;
! Gets a host name for the Internet address specified by InAddr.
The following example uses String2NetAddr to fill in a TInAddr structure from a text string
containing an Internet address. It then calls LookupAddress to get the host name for the
address.
var
MyAddr : TInAddr;
with TApdSocket.Create(self) do try
MyAddr := String2NetAddr('165.212.210.12');
HostLabel.Caption := LookupAddress(MyAddr);
finally
Free;
end;
See also: LookupName, String2NetAddr
10
11
12
13
14
LookupName
function LookupName(const Name : string) : TInAddr;
TInAddr = packed record
case Integer of
0 : (S_un_b : SunB);
1 : (S_un_w : SunW);
2 : (S_addr : LongInt);
end;
! LookupName gets an Internet address for the host name specified by Name.
The Internet address is returned as a TInAddr structure.
15
16
17
122 Chapter 3: Winsock Components
1
1
method
The following example gets an Internet address from the host name
“www.turbopower.com” and then uses the NetAddr2String method to display the address in
a label:
1
2
var
MyAddr : TInAddr;
with TApdSocket.Create(Self) do try
MyAddr := LookupName('www.turbopower.com');
AddressLabel.Caption := NetAddr2String(MyAddr);
finally
Free;
end;
3
4
See also: LookupHost, NetAddr2String
LookupPort
method
6
function LookupPort(Port : Integer) : string;
7
! Gets a text string of the service name for the port specified by Port.
There are certain well-known ports used in Winsock. For example, port 25 is typically used
for SMTP (mail), port 23 is used for telnet, and port 119 is used for NNTP (news):
with TApdSocket.Create(Self) do try
ServiceLabel.Caption := LookupPort(25);
finally
Free;
end;
8
9
10
See also: LookupService
11
12
13
14
15
16
17
TApdSocket Component 123
1
1
1
LookupService
method
function LookupService(const Service : string) : Integer;
2
3
4
5
6
7
! Gets the port number for the service name specified by Service.
The service name should be one of the Winsock well-known services (such as “SMTP”). If
the service cannot found, LookupService returns an empty string.
var
MyPort : Integer;
with TApdSocket.Create(Self) do try
MyPort := LookupService('smtp');
finally
Free;
end;
See also: LookupPort
MaxSockets
read-only, run-time property
property MaxSockets : Word
8
9
! The maximum number of sockets available for the current version of Winsock.
NetAddr2String
function NetAddr2String(InAddr : TInAddr) : string;
10
11
12
13
TInAddr = packed record
case Integer of
0 : (S_un_b : SunB);
1 : (S_un_w : SunW);
2 : (S_addr : LongInt);
end;
! Translates the 32-bit network address in InAddr to a string.
The string is in dot notation (e.g., “165.212.210.12”).
14
15
16
17
124 Chapter 3: Winsock Components
1
1
method
The following example converts an Internet address to a string. The string is then displayed
in a label component.
var
MyAddr : TInAddr;
with TApdSocket.Create(Self) do try
MyAddr := LookupName('www.turbopower.com');
AddressLabel.Caption := NetAddr2String(MyAddr);
finally
Free;
end;
1
2
3
4
See also: String2NetAddr
ntohl
method
6
function ntohl(NetLong : LongInt) : LongInt;
! Translates a 32-bit value from network byte order to host byte order.
IBM-compatible computers typically store data in memory in little-endian byte order (the
least significant byte stored first followed by the most significant byte) or host byte order.
TCP/IP stipulates that data should be sent in big-endian byte order (most significant byte
followed by least significant byte) or network byte order.
8
9
See also: htonl, htons, ntohs
ntohs
7
method
10
function ntohs(NetShort : Word) : Word;
11
! Translates a 16-bit value from network byte order to host byte order.
IBM-compatible computers typically store data in memory in little-endian byte order (the
least significant byte stored first followed by the most significant byte) or host byte order.
TCP/IP stipulates that data should be sent in big-endian byte order (most significant byte
followed by least significant byte) or network byte order.
12
13
See also: htonl, htons, ntohl
14
15
16
17
TApdSocket Component 125
1
1
1
OnWsAccept
event
property OnWsAccept : TWsNotifyEvent
2
3
TWsNotifyEvent = procedure(
Sender: TObject; Socket: TSocket) of object;
! Defines an event handler that is called when the server accepts a connection.
4
This event is primarily used when an application is operating as a server. The server
application listens on a specific port for possible connections. When a client socket tries to
connect, the OnWsAccept event is generated.
5
See also: OnWsConnect
OnWsConnect
6
7
8
9
event
property OnWsConnect : TWsNotifyEvent
TWsNotifyEvent = procedure(
Sender: TObject; Socket: TSocket) of object;
! Defines an event handler that is called when a Winsock connection is established.
When a server application accepts a connection, the OnWsConnect event is generated to
notify the client application.
See also: OnWsDisconnect
10
11
12
OnWsDisconnect
property OnWsDisconnect : TWsNotifyEvent
TWsNotifyEvent = procedure(
Sender: TObject; Socket: TSocket) of object;
! Defines an event handler that is called when a Winsock connection is dropped.
13
A connection can be dropped as the result of an error or when a transmission is complete
and one end terminates the connection.
14
See also: OnWsConnect
15
16
17
126 Chapter 3: Winsock Components
1
1
event
OnWsError
event
1
property OnWsError : TWsSocketErrorEvent
2
TWsSocketErrorEvent = procedure(Sender : TObject;
Socket : TSocket; ErrorCode : Integer) of object;
! Defines an event handler that is called when a Winsock error occurs.
3
Socket identifies the socket for which the error occurred. ErrorCode contains the Winsock
error code. See “Error Handling and Exception Classes” on page 900 for a list of the error
codes.
4
See Also: LastError
OnWsRead
event
6
property OnWsRead : TWsNotifyEvent
TWsNotifyEvent = procedure(
Sender : TObject; Socket : TSocket) of object;
7
! Defines an event handler that is called when data is available to be read on a socket.
8
See also: OnWsWrite
OnWsWrite
event
property OnWsWrite : TWsNotifyEvent
10
TWsNotifyEvent = procedure(
Sender : TObject; Socket : TSocket) of object;
! Defines an event handler that is called when Winsock can accept more data from a socket.
See also: OnWsRead
ReadSocket
9
11
12
method
13
function ReadSocket(
Socket : TSocket; var Buf; BufSize, Flags : Integer) : Integer;
! Reads data from a socket.
Socket is the socket from which to receive data. Buf is the buffer where the data is stored.
BufSize is the size of Buf in bytes. Flags determines how the receive operates. Set Flags to
zero for normal operation, or see your Winsock documentation for other possible values.
ReadSocket returns the number of bytes read.
See also: WriteSocket
14
15
16
17
TApdSocket Component 127
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
SetAsyncStyles
function SetAsyncStyles(
Socket : TSocket; lEvent : LongInt) : Integer;
! Tells Winsock to send notification of certain network events.
Socket is the socket for which events should be reported. lEvent is the event or events that
should be reported. The notification event constants for which you can receive notification
are FD_READ, FD_WRITE, FD_CONNECT, and FD_ACCEPT. See the WSAAsyncSelect
function in your Winsock documentation for more information.
When an event that notification is requested for occurs, Winsock sends a
CM_APDSOCKETMESSAGE message to the TApdSocket class. The low word of lParam is
the network event that occurred and the high word is an error code if an error occurred.
String2NetAddr
TInAddr = packed record
case Integer of
0 : (S_un_b : SunB);
1 : (S_un_w : SunW);
2 : (S_addr : LongInt);
end;
! Translates a string to a network address.
String2NetAddr translates S into a 32-bit value in network byte order. It is returned in the
form of a TInAddr structure. S should be in dot notation (e.g., “165.212.210.12”).
The following example creates a socket and then turns the string address “165.212.210.12”
into an network address. The network address could then be used to connect to a server.
var
MyAddr : TInAddr;
with TApdSocket.Create(Self) do try
MyAddr := String2NetAddr('165.212.210.12');
finally
Free;
end;
See also: Net2StringAddr
17
128 Chapter 3: Winsock Components
1
method
function String2NetAddr(const S : string) : TInAddr;
16
1
method
SystemStatus
read-only, run-time property
1
property SystemStatus : string
! Contains the current status of the Winsock DLL.
2
SystemStatus usually returns “Running under” Windows 95/98/ME or Windows NT
4.0/2000.
3
See also: Description
WriteSocket
method
4
function WriteSocket(
Socket : TSocket; var Buf; BufSize, Flags : Integer) : Integer;
! Sends data to a socket.
6
Socket is the socket on which to send data. Buf is the buffer that contains the data. BufSize is
the size of Buf in bytes. Flags determines the send method. Set Flags to zero for normal
operation or see your Winsock documentation for other possible values.
WriteSocket does not send the data directly to the receiving end. Winsock queues the data
and sends it when possible. The return value from WriteSocket is the number of bytes
queued for transmission.
See also: ReadSocket
WsVersion
read-only, run-time property
7
8
9
10
property WsVersion : Word
11
! Contains the version number of the current Winsock DLL.
WsVersion is a 16-bit value. The high-order byte contains the major version number and the
low-order byte contains the minor version number.
12
13
14
15
16
17
TApdSocket Component 129
1
1
1
2
3
4
5
The following example gets the version number, translates it into a text string, and displays it
in a label component:
var
MyVer : Word;
with TApdSocket.Create(Self) do try
MyVer := WsVersion;
VerLabel.Caption := Format('%d.%d', [LoByte(MyVer),
HiByte(MyVer)]);
finally
Free;
end;
See also: HighVersion
6
7
8
9
10
11
12
13
14
15
16
17
130 Chapter 3: Winsock Components
1
1
Chapter 4: Data Packet Component
1
2
The purpose of the data packet component is to provide a simple solution to the common
task of looking for a particular sequence of bytes in the incoming data. Data packet
components collect data that has certain properties and pass that data as a complete unit to
the client application.
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
131
1
1
1
TApdDataPacket Component
2
The TApdDataPacket component provides automatic data packet delivery from the
incoming data stream based on simple properties set in the component.
3
A data packet can be thought of as an advanced data trigger. Packets automatically collect
data from the incoming data stream based on criteria specified in the properties of the data
packet component, and deliver the data when the criteria have been met. As opposed to
traditional data triggers, data packets do their own buffering. This means that data packets
do not have the same limitation as data triggers (that data may no longer be available in the
input buffer for processing when the data trigger fires).
4
5
6
You would typically use data packets in place of data triggers when the data you are looking
for has a fixed length or starts or ends with a known string of data. These conditions can be
set in the data packet component at design time or run time.
7
Data as packets
8
9
10
11
Most data arriving at the serial port can be described as a packet. It will have a start
condition (something that defines the beginning of the data) and an end condition
(something that defines the end of the data). The TApdDataPacket component supports
start conditions of a character or characters, or any data. The TApdDataPacket component
supports end conditions of a character or characters, timeout, or a specific number of
characters. The simplest data packet is a single string, such as “hello”, and can progress
through more complicated packets that define other packets within the collected data. In
broad strokes, packets can be categorized as follows:
• A specific character/string: The StartString defines the packet in its entirety. Set
StartCond to scString (the default) and StartString to the string to detect:
12
13
14
15
...
ApdDataPacket1.StartCond := scString;
ApdDataPacket1.StartString := 'hello';
...
• A bracketed packet: This is the most common usage, where the beginning and ending
of the data is defined by known characters/strings. For example, you may be
expecting data starting with an <STX> (#2) character, followed by a chunk of data,
16
17
132 Chapter 4: Data Packet Component
1
1
terminated by an <ETX> (#3) character. Set StartCond to scString, StartString to the
“start of packet” character/string, EndCond to [ecString] and EndString to the “end
of packet” character/string:
...
ApdDataPacket1.StartCond := scString;
ApdDataPacket1.StartString := #2; // STX char
ApdDataPacket1.EndCond := [ecString];
ApdDataPacket1.EndString := #3; // ETX char
...
1
2
3
4
Note: C++Builder uses a more difficult implementation of sets than Delphi does, use the
following syntax to set the EndCond property:
...
ApdDataPacket1->EndCond.Clear();
ApdDataPacket1->EndCond << ecString;
...
6
• A known start character/string followed by data of known length: An example might
7
be an <STX> (#2) followed by 18 characters without a terminating character/string.
To detect this packet, set StartCond to scString, StartString to the “start of packet”
character/string, EndCond to [ecPacketSize] and PacketSize to the length of the data.
If IncludeStrings is True, add the length of your StartString to PacketSize.
8
...
ApdDataPacket1.StartCond := scString
ApdDataPacket1.StartString := #2; // STX char
ApdDataPacket1.EndCond := [ecPacketSize];
ApdDataPacket1.IncludeStrings := True;
ApdDataPacket1.PacketLength := 19; // 18 data chars,
1 start char
...
• A known number of data chars, with a terminating character/string: An example of
this type of packet could be a log in sequence, where you would prompt for a user
name and want to collect everything up to a <CR>.
...
ApdDataPacket1.StartCond := scAnyData;
ApdDataPacket1.EndCond := [ecString];
ApdDataPacket1.EndString := #13; // CR char
...
These are only a few of the possibilities. Your data may vary. You may have a start string,
followed by a character indicating the length of the data (use two data packets, one to collect
the start string and length char, the other to capture the next “length” number of chars), or
9
10
11
12
13
14
15
16
17
TApdDataPacket Component 133
1
1
1
2
3
4
5
6
you may have something that needs a more liberal packet interpretation. If you can
conceptualize the expansibility of the packet format, you can usually work something up
that works for your conditions.
Data ownership
There is no limit on the number of data packet components for a port, however, any
incoming character can be part of only one data packet. The first enabled data packet that
has its start condition met takes ownership of all incoming data until the packet is complete.
If a data packet times out, the data it has collected up to that point is made available to any
other enabled data packets for the port.
The TApdDataPacket component has a component editor, shown in Figure 4.1, where all
properties can conveniently be set at once. You can invoke it by right clicking on the contextmenu of the component.
7
8
9
10
11
12
13
Figure 4.1: TApdDataPacket component editor.
14
Packet Start Condition
15
The Packet Start Condition defines the start of the packet. You have the option to start the
packet as soon as any data is received or you can start data collection when a particular
string is received.
16
Refer to the StartCond and StartString properties in the reference section for more
information on starting a packet.
17
134 Chapter 4: Data Packet Component
1
1
Packet End Condition
The Packet End Condition defines when the packet is complete. Packet completion can
either occur after a certain number of characters have been received, or when a particular
string is received to terminate the packet. If both types of conditions are defined, the first
condition met will cause the packet delivery event to fire.
1
2
Refer to the EndCond, EndString and PacketSize properties in the reference section for
more information on terminating a packet.
3
Additional properties
4
The additional properties define details about how the packet should operate: Whether it
should be initially enabled, whether it should automatically re-enable after having been
received (the default is that it re-enables itself), whether case should be ignored on the start
and end strings, whether or not the start and end strings should be included in the packet
delivered in the OnPacket events and whether the packet collection logic can time out for
this packet and what the time-out period should be.
Refer to the AutoEnable, IgnoreCase, IncludeStrings and TimeOut properties in the
reference section for more information on these additional properties.
6
7
8
Non-printable characters and wildcards in the packet
The TApdDataPacket supports some translations in the StartString and EndString
properties. These include support for non-printable characters (control chars) and
wildcards. To specify a control character, you can use either caret (‘^’) or decimal notation,
or you can enter the literal char at run time. For example, to add the <ACK> character, you
can enter “^F” or “#6” at design time, or just #6 at run time. Since a caret (‘^’) and pound
(‘#’) are control character escapes, they must be enclosed in quotes to detect a literal ‘^’ in
the data stream.
10
Wildcards in the packet definition can be very useful. The wildcard character is a single
question mark (‘?’). This is interpreted as any single character. Since a single ‘?’ is now
considered a wildcard, use “\?” to detect a literal ‘?’ in the data stream.
12
For example, a relatively common packet will contain a block of data terminated by a
character that is followed by a checksum character. This can be captured by setting the
EndString to the terminating character and a ‘?’:
...
ApdDataPacket1.EndString := #3 + '?'; // ETX and the next char
...
If IncludeStrings is True, the last char in the collected data will be the checksum.
9
11
13
14
15
16
17
TApdDataPacket Component 135
1
1
1
Example
2
This example demonstrates the use of a TApdDataPacket component to retrieve a modem’s
response to the ATI3 command. Create a new project, add the following components, and
set the property values as indicated in Table 4.1.
3
Table 4.1: Example components and property settings
Component
4
Property
Value
TApdComport
TApdDataPacket
5
6
StartString
‘ATI3’#13
EndString
‘OK’#13
EndCond
[ecString]
IncludeStrings
False
TButton
7
8
9
10
11
12
13
Double-click on the button’s OnClick event handler in the Object Inspector and modify the
generated source code to match this:
procedure TForm1.Button1Click(Sender : TObject);
begin
ApdComPort1.PutString('ATI3'#13);
end;
Double-click on the TApdDataPacket component OnStringPacket event handler in the
Object Inspector and modify the generated source code to match this:
procedure TForm1.ApdDataPacket1StringPacket(
Sender : TObject; Data : String);
begin
Caption := trim(Data);
end;
Compile and run the application. When prompted, select a serial port that has a modem
attached. When you press the button, you should see the caption change to the data set
name (the response to ATI3) reported by the modem.
14
15
16
17
136 Chapter 4: Data Packet Component
1
1
Hierarchy
1
TComponent (VCL)
! TApdBaseComponent (OOMisc) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
TApdDataPacket (AdPacket)
2
3
Properties
AutoEnable
EndString
StartCond
ComPort
IgnoreCase
StartString
Enabled
IncludeStrings
EndCond
PacketSize
4
TimeOut
! Version
6
Events
OnPacket
OnTimeout
OnStringPacket
7
8
9
10
11
12
13
14
15
16
17
TApdDataPacket Component 137
1
1
1
Reference Section
AutoEnable
property
2
property AutoEnable : Boolean
3
Default: True
! Determines whether a data packet is automatically re-enabled.
4
5
6
AutoEnable controls what happens after the packet is received (the start string and end
condition for the packet are met in the data stream). If AutoEnable is True, the data packet is
enabled again and TApdDataPacket starts watching for the start string again. If AutoEnable
is False, the data packet is disabled.
ComPort
property
property ComPort : TApdCustomComPort
7
8
! Determines the port used by the data packet.
Enabled
property
property Enabled : Boolean
9
10
11
Default: True
! Determines whether a packet is allowed to collect data.
You can have as many TApdDataPacket components as you like. You can conveniently turn
them on or off using the Enabled property. Changing the Enabled property is allowed from
within event handlers. This lets you chain packets together. See the QRYMDM example
program for an example of how to do this.
12
13
14
15
16
17
138 Chapter 4: Data Packet Component
1
1
EndCond
property
1
property EndCond : TPacketEndSet
2
TPacketEndSet = set of TPacketEndCond;
TPacketEndCond = (ecString, ecPacketSize);
3
Default: []
! Determines when a complete packet has been received.
4
The valid values for EndCond are:
Value
Result
[]
The string specified by StartString is.
considered to be the entire packet (this is
equivalent to a traditional data trigger).
[EcString]
The packet ends when the string specified
by EndString is received.
[EcPacketSize]
The packet ends when the number of
characters specified by PacketSize is
received.
[EcString and ecPacketSize]
The packet ends when either the string
specified by EndString is received or the
number of characters specified by
PacketSize is received.
Note: C++Builder uses a more difficult implementation of sets than Delphi does, use the
following syntax to set the EndCond property:
6
7
8
9
10
11
...
ApdDataPacket1->EndCond.Clear();
ApdDataPacket1->EndCond << ecString;
...
12
See also: EndString, PacketSize, StartString
13
14
15
16
17
TApdDataPacket Component 139
1
1
1
EndString
property
property EndString : string
2
! The string that completes a data packet.
3
If EndCond contains ecString, the packet stops collecting data when the string specified by
EndString is received.
4
See the StartString property on page 143 for information about specifying characters and
using fixed-length wildcards in the string.
See also: EndCond, StartString
5
6
IgnoreCase
property
property IgnoreCase : Boolean
Default: True
7
8
! Determines whether the StartString and EndString properties are case-sensitive.
See also: EndString, StartString
IncludeStrings
9
10
11
12
property IncludeStrings : Boolean
Default: True
! Determines whether the strings that define a packet are made available to the event handler.
For example, assume that StartString is “Async”, EndString is “al”, and the string “Async
Professional” arrives at the port. If IncludeStrings is True, the packet will contain “Async
Professional.” If IncludeStrings is False, the packet will contain “Profession.”
See also: EndString, StartString
13
14
15
16
17
140 Chapter 4: Data Packet Component
1
1
property
OnPacket
event
1
property OnPacket : TPacketNotifyEvent
2
TPacketNotifyEvent = procedure(
Sender : TObject; Data : pointer; Size : Integer) of object;
! Defines an event handler that is called when a complete data packet is available.
3
Data is a pointer to the actual collected data. Size is the length of the collected data. The data
at Data is only valid for the duration of this event. Since Data is temporary, you can move the
collected data into your own buffer for storage or further processing outside of this event.
The following snippet demonstrates one technique of doing this:
4
var
Buffer: array[0..255] of byte;
6
procedure TForm1.ApdDataPacket1Packet(
Sender: TObject; Data: Pointer; Size: Integer);
begin
Move(Data^, Buffer[0], Size);
end;
7
8
void __fastcall TForm1::ApdDataPacket1Packet(
TObject *Sender, Pointer Data, int Size)
{
char* MyData = new char[Size];
Move(Data, MyData, Size);
}
OnTimeout
9
10
event
11
property OnTimeout : TNotifyEvent
! Defines an event handler that is called when a timeout occurs during receipt of a packet.
12
The OnTimeout event is generated when a packet is in data collection mode but hasn’t
completed within the number of ticks specified by TimeOut. By default, packets are disabled
when they time out, but they can be re-enabled from within the event handler if desired.
13
The data collected up to the point of the timeout is available through the GetCollectedString
and GetCollectedData methods.
14
The timeout timer does not start until the start condition has been met. If StartCond =
scString, the timer starts once the string defined by StartString has been received. If
StartCond = scAnyData, the timer starts once the data packet has been enabled. If you need
to start the timer once the packet starts actually collecting data, set the StartCond to scString
and StartString to the wildcard char (‘?’).
15
16
17
TApdDataPacket Component 141
1
1
1
OnStringPacket
event
property OnStringPacket : TStringPacketNotifyEvent
2
3
4
TStringPacketNotifyEvent = procedure(
Sender : TObject; Data : string) of object;
! Defines an event handler that is called when a complete data packet is available.
Data is the actual data in the packet. The data packet is only available for the duration of
the event.
5
Note that a null character (#0) in the collected data may terminate the Data string
prematurely. This is due to the way that Delphi and C++Builder implement huge strings. If
you are expecting null characters in the collected data, use the OnPacket event instead.
6
See also: OnPacket
PacketSize
7
8
property
property PacketSize : Integer
Default: 0
! Determines the size of a packet.
9
10
11
12
If EndCond contains ecCharCount, PacketSize determines the size of the data packet.
If IncludeStrings is True, PacketSize will not compensate for the length of the start and end
strings. For example, assume that the StartString is “Async”, PacketSize is 13 and the string
“Async Professional” arrives at the port. If IncludeStrings is True, the collected data will
contain “Async Profess” (13 characters); if IncludeStrings is False, the collected data will
contain “Professional.”
See also: EndCond
13
14
15
16
17
142 Chapter 4: Data Packet Component
1
1
StartCond
property
1
property StartCond : TPacketStartCond
2
TPacketStartCond = (scString, scAnyData);
Default: scString
3
! Determines when a packet should start collecting data.
The valid values for StartCond are:
4
Value
Result
scString
The packet starts collecting data when the string specified
by StartString is received.
scAnyData
The packet starts collecting data as soon as data is
available in the input queue.
6
See also: StartString
StartString
property
7
8
property StartString : string
! The string that causes a packet to start collecting data.
If StartCond is scString, the packet starts collecting data when the string specified by
StartString is received.
To specify a control character in the string, use a caret ‘^’ symbol (e.g. ^L^M). To specify a
character with ordinal value greater than 127, use the #nnn notation, where nnn is an integer
in the range 128 to 255. Since ‘^’ and ‘#’ are used in this special way as escape characters, if
you want a ‘^’ or ‘#’ as a printable character in the string, it must be enclosed in quotes. To
mix printable and non-printable characters in a string, enclose the printable characters in
quotes.
The following example sets the StartString to “123 #”, followed by a <Ctrl C>, followed by
“Sample ^ ^”, followed by the unprintable character 255:
9
10
11
12
13
ApdDataPacket.StartString := '123 # '^C'Sample ^ ^ '#255;
14
15
16
17
TApdDataPacket Component 143
1
1
1
2
3
4
5
6
StartString also supports fixed-length wildcards. The character ? within a string is
interpreted as a wildcard character place-holder which will match any character in the input
stream. Wildcards can occur anywhere in the StartString and EndString properties,
including at the beginning or end of the strings. For example:
'ATI?'^M^J will match 'ATI0^M^J, 'ATI1^M^J...,
'END??' will match 'END12', 'END99'...,
'??BEGIN' will match 'AABEGIN', 'BBBEGIN'..., etc.
Since ? is now interpreted as a wildcard, an actual ? in the packet must be escaped by \
(backslash). To specify an actual \, use \\. For example:
'+FMFR\?' really means '+FMFR?' where the ‘?’ is a literal ‘?’.
'\\ASC' really means '\ASC' where the ‘\’ is a literal ‘\’.
See also: EndString, StartCond
TimeOut
7
8
property
property TimeOut : Integer
Default: 2184 (~ 2 minutes)
! Determines how long a data packet waits for completion of a data stream.
9
10
11
12
13
If TimeOut is non-zero, it determines how long (in ticks) a data packet is allowed to wait for
completion after it has started collecting data. After TimeOut ticks, the data packet
relinquishes ownership of the data stream. If TimeOut is zero, the data packet holds
ownership of the data stream until the EndString is received.
The timeout timer does not start until the start condition has been met. If StartCond =
scString, the timer starts once the string defined by StartString has been received. If
StartCond = scAnyData, the timer starts once the data packet has been enabled. If you need
to start the timer once the packet starts actually collecting data, set the StartCond to scString
and StartString to the wildcard char (‘?’).
See “Data ownership” on page 134 for more information.
See also: EndString, OnTimeOut
14
15
16
17
144 Chapter 4: Data Packet Component
1
1
Chapter 5: Script Component
1
2
This chapter describes the TApdScript component, which contains properties and methods
for automating basic communications sessions.
3
A script is a list or file containing communications commands. Script languages are often
provided by general-purpose communications programs to automate standard operations
like logging on and off, file upload, and file download. The scripting support in TApdScript
provides similar, though much simpler, script facilities for Async Professional applications.
4
5
6
7
8
9
10
11
12
13
14
15
16
17
145
1
1
1
TApdScript Component
2
3
The AdScript unit provides a single documented component: TApdScript. TApdScript
implements a script language of about a dozen commands. While you wouldn’t want to
build a complete BBS using just these commands, they provide enough features to automate
and simplify many standard tasks.
4
The script language
The basic syntax of the script language is shown in the following line of code:
5
6
7
8
9
10
11
12
13
<command> <data1> <data2>;<comment>
In this line of code, <command> describes the action to perform, <data1> and <data2> are
optional arguments, and <comment> is an optional comment. The format of the arguments
vary among commands. The various components of each line must be separated by at least
one space or a comma. Additional spaces are permitted, but ignored. Commands are not
case-sensitive.
The following is a list of supported commands followed by brief descriptions and
discussions of the relationships between various commands:
:<label>
GOTO <label>
DISPLAY 'XX XX'
;<comment>
SENDBREAK <duration in ms>
INITPORT <1..99>
DELAY <duration in ms>
IF CONNECTED <label>
SET <option> <data>
DONEPORT
UPLOAD <protocol>
SEND 'XXXXXX'
DOWNLOAD <protocol>
CHDIR <pathname>
DELETE <filemask>
RUN <command> <wait>
EXIT <exitcode>
IF SUCCESS <label>
WAIT 'XXXX' <timeout in ms>
IF TIMEOUT <label>
IF FAIL <label>
IF 1,2,3...127
WAITMULT 'XXX|YYY|ZZZ' <timeout in ms>
:<label>
14
15
A point in the script file that can be jumped to via a GOTO or IF instruction. A label name
can be any type of string without embedded spaces. For example “:TopOfLoop”,
“:TOP_OF_LOOP” are both acceptable; “:top of loop” is not.
;<comment>
16
Any line that starts with a semicolon is considered a comment. Blank lines are also
considered comments and may be freely added for readability.
17
146 Chapter 5: Script Component
1
1
INITPORT <Com1..Com99>
Opens the specified port. Only one port at a time may be opened. This number directly
correlates with the ComNumber property of the TApdComPort component.
DONEPORT
Closes a port previously opened with INITPORT.
1
2
3
SEND 'XXXXXX'
Transmits the string “XXXXXX”. Control characters may transmitted by preceding a
character with ‘^’. For example, a control C character is represented by “^C”. You’ll use this
feature most often when sending carriage returns. For example, SEND “myname^M” might
be an appropriate response to a logon prompt where you would normally type your name
and press Enter.
Note: Unlike Object Pascal, control characters must be inside the quote marks, if quote
marks are necessary.
If the string does not contain any embedded blanks the beginning and ending quotes can be
omitted. The quotes are required if the string has embedded blanks. For example:
SEND ABC
4
5
6
7
8
sends ABC
SEND ‘ABC’ sends ABC
9
SEND A B C sends only the A ('B C' is considered a comment)
SEND ‘A B C’ sends A B C
10
WAIT 'XXXXX' <timeout in ms>
Waits up to <timeout in ms> milliseconds for a particular received string. The string
comparison is always case insensitive. However, the string comparison need not be
complete. If, for example, a host returns the string “Host XXXX ready” where XXXX might
vary from session to session, the WAIT command should wait for “ready” only. As with the
SEND command, beginning and ending quotes are only required if the string contains
embedded blanks.
This command sets one of three conditions: SUCCESS, FAIL or TIMEOUT, which can be
tested with the IF command. SUCCESS is set if the string is received before the timeout.
TIMEOUT is set if the timeout expires before the string is received. FAIL is set if the timeout
expires and all retries are exhausted.
11
12
13
14
15
IF SUCCESS/TIMEOUT/FAIL <label>
Tests the condition set by the last command and, if the tested condition is True, script
execution jumps to <label>. If the condition is not True then execution continues with the
next statement.
16
17
TApdScript Component 147
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
WAITMULTI 'XXX|ZZZ|YYY', <timeout in ms>
Waits up to <timeout in ms> milliseconds for one of several substrings. The bar character
(|) separates the substrings. The comparisons are always case insensitive. The maximum
length of the entire string is 255 characters. As with the SEND command, beginning and
ending quotes are only required if the string contains embedded blanks.
This command sets a numeric condition result based on the substring received: ‘1’ is set if
the first substring is received, ‘2’ is set if the second substring is received, and so on. If none
of the strings are received, then TIMEOUT is set. If all retries have been exhausted, then
FAIL is set.
IF 1,2,3...127 <label>
Tests the condition set by the last WAITMULTI command and, if the tested condition is
True, script execution jumps to <label>. If the condition is not True, then execution
continues with the next statement.
The following example sends a modem dial command, then waits for one of CONNECT,
NO CARRIER, or BUSY responses. If none of the responses are received then control falls
through to the GOTO statement:
send 'atdt260-9726^m'
waitmulti 'connect|no carrier|busy' 60000
if 1 HandleConnect
if 2 HandleNoConnect
if 3 HandleBusy
goto HandleTimeout
:HandleConnect
...proceed with session
:HandleNoConnect
...handle noconnect error
:HandleBusy
...handle busy error
...
GOTO <label>
Unconditionally jumps to <label>.
DISPLAY 'Just did something'
Generates a call to the TApdScript component’s OnScriptDisplay event handler. If the
DisplayToTerminal property is True and a terminal component exists on the form, then the
string is also displayed to the terminal. This can be used to monitor the progress of the script
and to aid in debugging.
17
148 Chapter 5: Script Component
1
1
SENDBREAK <duration in ms>
1
Transmits a break of <duration in ms> milliseconds.
DELAY <duration in ms>
2
Delays for <duration in ms> milliseconds. The script doesn’t yield during delays so you
should keep the delays as short as possible.
3
SET <option> <data>
Sets or resets a variety of port, script and protocol options. Some options require an
additional argument, others do not. Table 5.1 shows a list of all options.
5
Table 5.1: SET options
BAUD <number>
DATABITS <5,6,7,8>
FLOW <RTS/CTS,XON/XOFF,NONE>
4
Sets the Baud property of the
associated TApdCustomComPort
component. For further information
regarding the allowable values,
refer to the Baud property of
TApdComPort.
Sets the DataBits property of the
associated TApdCustomComPort
component. Allowable values are 5, 6,
7 or 8.
Sets flow control options for the
associated TApdCustomComPort
component. Allowable values are
RTS/CTS for hardware flow control,
XON/XOFF for software flow control,
and NONE to turn off all flow
control.
PARITY <NONE,ODD,EVEN,MARK,SPACE>
Sets the Parity property of the
associated TApdCustomComPort
component. Allowable values are
NONE, ODD, EVEN, MARK or SPACE.
STOPBITS <1,2>
Sets the StopBits property of the
associated TApdCustomComPort
component. Allowable values are 1 and
2.
6
7
8
9
10
11
12
13
14
15
16
17
TApdScript Component 149
1
1
1
Table 5.1: SET options (continued)
RETRY <data>
Sets an internal retry count that is
incremented whenever WAIT or
WAITMULTI result in a TIMEOUT
condition. When <retry count>
TIMEOUTs have occurred the FAIL
condition is set. The default is 1,
meaning no retries are attempted.
DIRECTORY <pathname>
Sets the destination directory used
during protocol receives.
FILEMASK <filemask>
Sets the file mask used during
protocol file transfers. For nonbatch protocols this must be a
specific file name rather than a
mask.
FILENAME <filename>
Sets the received file name for
protocols that do not transfer the
file name (all Xmodem protocols).
WRITEFAIL
Sets the WriteFailAction property to
wfWriteFail for all protocols except
Zmodem. This means that if an
incoming file already exists the
incoming file is skipped.
WRITERENAME
Sets the WriteFailAction property to
wfWriteRename for all protocols
except Zmodem. This means that if an
incoming file already exists the
incoming file is renamed (the first
character of the file name is
replaced with $).
WRITEANYWAY
Sets the WriteFailAction property to
wfWriteAnyway for all protocols
except Zmodem. This means that if an
incoming file already exists the
existing file is overwritten.
ZWRITECLOBBER
Sets the ZmodemFileOption property
to zfoWriteClobber. This means that
if an incoming file already exists
the existing file is overwritten.
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
150 Chapter 5: Script Component
1
1
Table 5.1: SET options (continued)
ZWRITEPROTECT
ZWRITENEWER
ZSKIPNOFILE <True/False>
1
Sets the ZmodemFileOption property
to zfWriteProtect option. This means
that if an incoming file already
exists the incoming file is skipped.
2
Sets the ZmodemFileOption property
to zfWriteNewer option. This means
that if an incoming file already
exists the existing file is
overwritten only if the incoming file
is newer.
3
Sets the ZmodemSkipNoFile property
to True or False. When this option is
True incoming files are skipped if
they do not already exist on the
receiving machine.
5
4
6
7
UPLOAD <protocol>
Starts transmitting files using <protocol>. <protocol> must be one of the following:
XMODEM, XMODEM1K, XMODEM1KG, YMODEM, YMODEMG, ZMODEM or
KERMIT. All files matching the mask previously specified by SET FILEMASK are
transmitted.
8
9
DOWNLOAD <protocol>
Starts receiving files using <protocol>. <protocol> must be one of the following:
XMODEM, XMODEM1K, XMODEM1KG, YMODEM, YMODEMG, ZMODEM or
KERMIT. When using any of the Xmodem protocols, you must call SET FILENAME before
DOWNLOAD to specify the name of the received file.
10
CHDIR <pathname>
12
Changes the current directory to the one specified by <pathname>. If the directory does not
exist, the FAIL condition is set.
11
13
DELETE <filemask>
Deletes all files matching <filemask>. If no path is specified the current directory is used.
14
15
16
17
TApdScript Component 151
1
1
1
RUN <command> <wait>
2
Executes the specified command, batch file or program. <wait> can be True or False and
determines whether the script waits for the command to complete its execution. <wait> is
True by default.
3
Following is an example script showing how these commands might be used to log on to a
host or terminal server:
4
5
6
7
SET RETRY 10
:Again
SEND ^C
WAIT 'READY' 182
IF SUCCESS Logon
IF TIMEOUT Again
IF FAIL, Done
:Logon
SEND 'Name, password^M'
...
:Done
SEND 'Bye^M'
;Try 10 times
;Send an attention character
;Wait 10 seconds for response
;Got prompt, continue with logon
;Try again if we timed out
;Give up after 10 tries
;Send name and password
8
EXIT <exitcode>
9
This will terminate the script and return the exit code as the Condition parameter in the
OnScriptFinish event. If the exitcode is not specified, a Condition of SUCCESS will be
passed to the OnScriptFinish event.
10
The exitcode parameter can be SUCCESS, TIMEOUT, FAIL or an integer value.
11
User functions and variables
12
13
User functions and variables are designed to provide a means by which your scripts can
interact with the host application. When a user function or user variable are encountered,
the script will fire events to either handle the event or to supply the value for the user
variable.
User functions
14
User functions are indicated by a ‘&’ as the first character in the name of the function. User
functions can take a single optional parameter.
15
When a user function is encountered in the script, it will generate the
OnScriptUserFunction event. This event will pass the name of the function converted to
upper case and with the leading ‘&’ removed in the Command parameter of the event. If a
parameter has been specified for the user function, this will be passed in the Parameter
parameter.
16
17
152 Chapter 5: Script Component
1
1
Following are examples of calls to user functions. In all cases, the OnScriptUserFunction
event will be called with a Command parameter of “MYFUNCTION.” In the first example,
the value of Parameter is empty. In the second and third examples, the value of Parameter is
the string “Parameter” and “1234” respectively:
&MyFunction
&MyFunction 'Parameter'
&MyFunction 1234
1
2
3
User variables
User variables are indicated by a ‘$’ as the first character in the name of the variable. User
variables can appear in any place where a parameter is expected and they can appear as the
parameter to a user function. In the latter case, the user variable will be evaluated before the
user function.
When a user variable is encountered in the script, it will generate the OnScriptUserVariable
event. The name of the user variable will be passed in the Variable parameter of the
OnScriptUserVariable event. The name of the variable will be exactly as it appears in the
script (including the leading ‘$’). You will need to specify the value of the variable in the
NewValue parameter of the event.
Following are examples of using user variables:
DISPLAY $MyVariable
&MyFunction $MyVariable
4
5
6
7
8
9
Executing scripts
10
A script is a list of commands in the format described in the previous section. The script can
be an external ASCII text file or can be contained within a TStringList component. Scripts
must be prepared with a call to PrepareScript before they can be executed. Preparing the
script translates (compiles) it into memory. Syntax errors cause the EApxScriptError
exception to be raised with one of the following error messages:
Not a valid script command. Line #
Bad format for first parameter. Line #
Bad format for second parameter. Line #
Label is referenced but never defined. Line #
Bad option in SET command. Line #
Error XXX while processing script. Line #
11
12
13
14
15
16
17
TApdScript Component 153
1
1
1
2
3
Scripts are always executed in the background in a fashion similar to file transfer protocols.
Scripts are started with a call to StartScript, which returns immediately. If no script
commands have been prepared (or can be prepared) StartScript raises the EApxScriptError
exception. If the script was started successfully it continues in the background until
completion (either successfully or due to an error). When the script is finished, it generates
the OnScriptFinish event.
Other components
4
5
6
7
8
9
10
11
12
The script component always needs a TApdCustomComPort component descendent (i.e.,
TApdComPort or a user created descendent). When a TApdScript component is created, it
searches the form for a TApdCustomComPort component and uses the first one it finds. If a
TApdCustomComPort isn’t found, StartScript creates one with all default values. In many
cases it’s best to create and customize the TApdCustomComPort yourself before starting the
script.
A program using a script may also use a terminal window (TAdTerminal). No special action
is required to associate the terminal with the TApdScript component. However, if the
TApdScript component finds a terminal component it does two things. One, it deactivates
the terminal during file transfers; and two, it sends the strings from DISPLAY commands to
the terminal if the script property DisplayToTerminal is True. Even if no DISPLAY
commands are used, the terminal window is useful during script development and
debugging because it displays all received data.
If the script calls either UPLOAD or DOWNLOAD a TApdProtocol component is required.
As with the TApdCustomComPort, the script will automatically create a TApdProtocol if it
can’t find one on the form. However, also like the TApdCustomComPort, the script provides
only a few commands to customize the protocol so it’s better to create the protocol
component yourself before starting the script.
Debugging scripts
14
A script file is really an interpreted program and TApdScript is the interpreter. Like any
program you write, script files may require a bit of debugging. Simple syntax errors will
always be detected and reported by PrepareScript. Logic errors, however, may require some
debugging effort on your part to find and correct.
15
TApdScript provides a variety of tools for debugging scripts. First, it provides three events
for tracking the progress of the script:
13
1. OnScriptCommandStart—called before each command is executed.
16
17
2. OnScriptCommandFinish—called after each command is executed.
3. OnScriptFinish—called when the entire script completes.
154 Chapter 5: Script Component
1
1
It also provides the OnScriptDisplay event, which is called in response to each DISPLAY
command in the script.
1
Finally, you can use the debug log available within the TApdComPort component to
examine the data sent and received during a script session.
2
Example
3
This example creates a very simple script file to send the ATI4 command to a modem and
wait for the OK response. Because this example includes a terminal window, the results of
the ATI4 command are displayed in that window.
4
Create a new project, add the following components, and set the property values as
indicated in Table 5.2.
5
Table 5.2: Script example property values
6
Component
Property
Value
TApdComPort
ComNumber
<as required>
7
ScriptCommands
“send ati4^m”
8
TAdTerminal
TApdScript
“wait ok 36”
TButton
Name
9
Start
Double-click on the Start button and modify the generated method to look like this:
procedure TForm1.StartClick(Sender: TObject);
begin
ApxScript1.StartScript;
end;
10
11
This method starts the script. StartScript returns immediately while the script continues
running in the background. Note that PrepareScript was not called. StartScript calls
PrepareScript itself when either ScriptCommands contains commands or ScriptFile
contains a file name. If the script contains any syntax errors, StartScript will raise an
EApdScriptError exception.
12
13
14
15
16
17
TApdScript Component 155
1
1
1
2
3
4
5
6
Double-click on the script component’s OnScriptFinished event handler in the Object
Inspector and modify the generated method to look like this:
procedure TForm1.ApdScript1ScriptFinish(
CP: TObject; Condition: Integer);
begin
ShowMessage('Script finished!');
end;
This method displays a message box when the script is finished.
Run the project and experiment with the generated program.
Hierarchy
TComponent (VCL)
! TApdBaseComponent (AdMisc). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
TApdCustomScript (AdScript)
7
8
9
10
11
12
13
TApdScript (AdScript)
Properties
ComPort
Protocol
DisplayToTerminal
ScriptCommands
InProgress
ScriptFile
Methods
CancelScript
PrepareScript
StartScript
OnScriptCommandStart
OnScriptDisplay
OnScriptParseVariable
OnScriptCommandFinish
OnScriptFinish
OnScriptUserFunction
Events
14
15
16
17
156 Chapter 5: Script Component
1
1
Terminal
! Version
Reference Section
1
CancelScript
method
2
procedure CancelScript;
! Cancels the background script.
3
Once a script is started, it executes all commands in the background without any help or
interference from the foreground process. The only way to stop the script, short of letting it
run to completion, is to call CancelScript. CancelScript stops the script and removes any
resources (i.e., triggers, event handlers) the script may have installed. CancelScript does not
free any components (TApdComPort or TApdProtocol) that the script may have created.
Those components are re-used in subsequent calls to StartScript and are freed only when the
script component is destroyed.
ComPort
4
5
6
property
7
property ComPort : TApdCustomComPort
! The comport component used by the script to send and receive data.
8
When the script is created it assigns the first comport component it finds on the form to
ComPort. ComPort is also automatically filled in if a comport is created after the script
component. If ComPort is unassigned when StartScript is called, StartScript dynamically
creates a TApdComPort component and fills in ComPort.
DisplayToTerminal
property
9
10
11
property DisplayToTerminal : Boolean
! When True, the script DISPLAY commands display data to the terminal window.
Set this to True if the application has a terminal window and the terminal window should be
used to display the strings from the script’s DISPLAY commands. If the application doesn’t
have a terminal window this property is ignored. If this property is set to False or the
application doesn’t have a terminal window, the application must implement an
OnScriptDisplay event handler in order to see the contents of the scripts’s DISPLAY
commands.
12
13
14
15
16
17
TApdScript Component 157
1
1
1
InProgress
run-time, read-only property
property InProgress : Boolean
2
! Returns True while a script is executing in the background.
3
Use this property to determine whether or not a script is executing in the background. A
typical use would be to prevent a user from starting a new script or any other
communications operation until the current script is finished.
4
OnScriptCommandFinish
5
property OnScriptCommandFinish : TScriptCommandEvent
6
7
8
9
! Generated after each script command is executed.
This event complements the OnScriptCommandStart event and can also be used to
implement single-stepping or for tracking the progress of the event. See
OnScriptCommandStart for a description of the types and constants used by
OnScriptCommandFinish.
For script commands that include a “wait” (WAIT, WAITMULTI, UPLOAD, DOWNLOAD)
OnScriptCommandFinish is generated after setting up for the command and before the
command actually finishes waiting. The indication that the command has finished waiting
(or transferring) is the OnScriptCommandStart of the next command.
See also: OnScriptCommandStart
10
11
12
13
14
15
16
17
158 Chapter 5: Script Component
1
1
event
OnScriptCommandStart
event
1
property OnScriptCommandStart : TScriptCommandEvent
2
TScriptCommandEvent = procedure(
CP : TObject; Node : TApdScriptNode;
Condition : SmallInt) of object;
TApdScriptNode = class(TObject)
Command : TApdScriptCommand; Data : String; Option : TOption;
Timeout : Word; Condition : Word;
TApdScriptCommand = (
scNone, scComment, scLabel, scInitPort, scDonePort, scSend,
scWait, scWaitMulti, scIf, scDisplay, scGoto, scSendBreak,
scDelay, scSetOption, scUpload, scDownload, scChDir, scDelete,
scRun, scExit);
3
4
5
6
! Generated before each script command is executed.
The primary purpose of this event is to provide a mechanism for single stepping through a
script file or for tracking the progress of a script. Node contains the command to be
executed. Node.Command is one of the TApdScriptCommand values shown above.
Condition contains the current condition code, one of ccXxx values shown above.
See also: OnScriptCommandFinish
OnScriptDisplay
7
8
9
event
10
property OnScriptDisplay : TScriptDisplayEvent
TScriptDisplayEvent = procedure(
CP : TObject; const Msg : String) of object;
11
! Generated in response to script DISPLAY commands.
12
The script processor doesn’t make any assumptions about how to display the contents of
DISPLAY commands. Instead, it generates an OnScriptDisplay event passing Msg, the string
to be displayed. The application can then display the string in whatever manner necessary.
One exception to this process occurs when the DisplayToTerminal property is True and the
script component finds a TAdTerminal window component on the form. In that case the
contents of the DISPLAY command are shown in the terminal window before
OnScriptDisplay is generated.
13
14
15
16
17
TApdScript Component 159
1
1
1
OnScriptFinish
event
property OnScriptFinish : TScriptFinishEvent
2
3
4
5
6
TScriptFinishEvent = procedure(
CP : TObject; Condition : SmallInt) of object;
! Generated at the end of the script.
This event is generated when the end of the script file or script list is reached and there are
no more script commands to execute. It is also generated if the script encounters a fatal error
during processing.
If an application dynamically creates a TApdScript component it should not free that
component, or any component that the script uses, during this event. Instead, it should set a
flag or post a message to itself noting that the script component can safely be destroyed at
some later point in the program.
See also: InProgress
7
OnScriptParseVariable
8
property OnScriptParseVariable : TScriptParseVariableEvent
9
TScriptParseVariableEvent = procedure(
CP : TObject; const Variable : String;
var NewValue : String) of object;
10
11
12
13
! Generated when a value is needed for a user variable.
This event is generated when a user variable is encountered in the script. User variables can
appear any place where a parameter is expected. They are indicated by a leading ‘$’. The
name of the user variable will be passed exactly as it is seen in the script (including the ‘$’).
In this event handler, the value of the user variable needs to be specified in the NewValue. If
this value is not specified, the value of the variable will be assumed to be blank.
Refer to the section “User functions and variables” on page 152 for more information.
See also: OnScriptUserFunction
14
15
16
17
160 Chapter 5: Script Component
1
1
event
OnScriptUserFunction
event
1
property OnScriptUserFunction : TScriptUserFunctionEvent
2
TScriptUserFunctionEvent = procedure(
CP : TObject; const Command : String;
const Parameter : String) of object;
3
! Generated when a user function has been encountered in the script.
This event is generated when a user function is encountered in the script. User functions are
indicated by a leading ‘&’. The name of the function will be passed to this event in the
Command parameter. This name will be converted to upper case and have the leading ‘&’
removed. Whatever functionality is needed to handle the user function should be
implemented in this event.
4
Refer to the section “User functions and variables” on page 152 for more information.
6
5
See also: OnScriptParseVariable
PrepareScript
method
7
8
procedure PrepareScript;
! Prepares the script command list and checks for syntax errors.
Before a list or file of script commands can be processed, it must first be prepared. This is
similar in concept to compiling a program. Each script command is read, checked for syntax
errors, and written to an internal list of compiled commands. When StartScript is called, it is
this internal list of compiled commands that is executed.
When PrepareScript finds a syntax error it raises an EApxScriptError exception with one of
the following error messages and the offending line number:
9
10
11
12
Not a valid script command. Line #
Bad format for first parameter. Line #
Bad format for second parameter. Line #
Label is referenced but never defined. Line #
Bad option in SET command. Line #
DOS error XXX while processing script. Line #
13
14
15
16
17
TApdScript Component 161
1
1
1
Protocol
property
property Protocol : TApdCustomProtocol
2
3
4
5
6
7
! Determines the protocol used by UPLOAD and DOWNLOAD commands.
When the script component is created it searches the form for an existing
TApdCustomProtocol and uses the first one it finds. If it doesn’t find any, it uses the first
TApdCustomProtocol later dropped onto the form.
If an UPLOAD or DOWNLOAD command is processed and Protocol is unassigned, the
script component creates a TApdProtocol component with default values. That protocol
component is destroyed when the script component is destroyed. The automatic creation of
the protocol component provides very little control over the protocol. The recommended
approach is for the application to create the protocol before the script is executed.
ScriptCommands
property
property ScriptCommands : TStrings
! The list of script commands.
8
Although declared as a TStrings component, this component is treated as a TStringList and
uses the TStringList property editor for adding and editing script commands at design time.
9
Note that ScriptCommands are used only when the ScriptFile property is empty. If the
ScriptFile contains a file name, then script commands are read from that file regardless of
the contents of ScriptCommands.
10
11
12
13
14
For convenience, at design time the script component loads the commands from ScriptFile
into ScriptCommands whenever a new ScriptFile property value is set. If the contents of
ScriptCommands are changed the script component automatically writes those changes to
ScriptFile, but only when the script component is destroyed or a new ScriptFile set. If the
contents of ScriptCommands is changed and the project is run without first closing, the
project the changes to ScriptCommands will be lost.
ScriptFile
property
property ScriptFile : String
! A file containing script commands.
15
When ScriptFile contains a file name PrepareScript and StartScript always read that file to
build/execute the script, ignoring the current contents ScriptCommands.
16
See ScriptCommands for more information concerning the relationship of
ScriptCommands and ScriptFile.
17
162 Chapter 5: Script Component
1
1
StartScript
method
1
procedure StartScript;
! Begins executing the script in the background.
2
StartScript checks the internal table of compiled commands. If that table is empty, it calls
PrepareScript to compile the commands in the file specified by ScriptFile. If ScriptFile is
empty, PrepareScript tries to compile the list of commands specified by ScriptCommands. If
that list is also empty, the EApdScriptError exception is raised.
If StartScript finds or creates a list of compiled commands, it begins executing those
commands. It continues executing commands until it encounters a command requiring a
“wait” (WAIT, WAITMULTI, UPLOAD, DOWNLOAD). It then sets up appropriate triggers
and trigger handlers and exits back to the application. When the triggers occur, the script
engine regains control in the background and continues executing until the next wait
command, when this process is repeated.
Terminal
property
property Terminal : TApdBaseWinControl
! Determines the terminal component used by the DISPLAY commands.
3
4
5
6
7
8
When the script component is created it searches the form for an existing terminal and uses
the first one it finds. If it doesn’t find any, it uses the first terminal later dropped onto the
form. Unlike comport and protocol components, the script component never creates a
terminal component.
10
If DisplayToTerminal is True, the contents of DISPLAY commands are written to this
terminal window.
11
9
12
13
14
15
16
17
TApdScript Component 163
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
164 Chapter 5: Script Component
1
1
Chapter 6: State Machine Components
1
2
A state machine is simply a device or technique that receives input and acts upon that input
based on the current condition of the device or technique. Async Professional uses state
machines for a variety of tasks; such as managing protocol file transfers, sending and
receiving faxes, collecting data packets, and monitoring the progress of a connection
attempt.
3
4
State machines are one of the fundamental techniques used for asynchronous
communications, where a command is transmitted and the reply is received later in the
session. Consider the simple task of initializing a modem to detect Caller ID information
and answering a call. The first step (after opening the correct serial port) is to send a generic
initialization command (“ATZ”<CR>), then wait for the modem to return either a success
response (“OK”) or a failure response (“ERROR”). The next step is to send the AT
command to enable Caller ID detection and text responses (“AT#CID=1”<CR>) and wait
for either a success or failure response. Finally, the project waits for the modem’s ring
indicator (“RING”), answers the call (“ATA”<CR>) and waits for the connection response
(“CONNECT”). Each instance of a command and response can be thought of as a separate
state in a state machine, as Table 6.1 illustrates.
5
6
7
8
Table 6.1: Simple State Machine
9
State
Output
Input
Next State
Send Init
"ATZ"<CR>
"OK"
Send CID
"ERROR"
Fail
"OK"
Wait for 1st ring
"ERROR"
Fail
Send CID
"AT#CID=1"<CR>
Wait for 1st ring
"RING"
Wait for 2nd ring
"RING"
Answer
Answer
"ATA"<CR>
"CONNECT"
Connected
"NO CARRIER"
Fail
Fail
Cleanup and report
the error
Connected
Continue the
session
10
11
12
Wait for 2nd ring
13
14
15
16
17
165
1
1
1
2
3
A simple, two or three-state state machine is relatively painless to create. The state machine
is usually driven by TApdDataPacket components monitoring for success or fail conditions.
When TApdDataPacket’s OnStringPacket event is generated, the state machine progresses
according to the collected data. Once a state machine grows to twenty or thirty states, with
multiple conditions defining the state progression, the project code can get cumbersome,
difficult to maintain, and hard to visualize.
6
The TApdStateMachine and TApdState components exist in order to assist in the
development of orderly, well-defined, and easy to maintain and visualize state machines.
The TApdStateMachine component is a container for TApdState components. TApdState
components contain conditions that determine the accepted input to drive the state
machine. To aid in the visualization of your state machine, the TApdStateMachine and
TApdState components are visual components, showing connection lines with captions and
customizable colors. When the state machine is executing, the currently active state is
highlighted to aid in debugging and to show the progression of the state machine.
7
State machine philosophy
4
5
8
9
10
11
12
13
14
15
16
In order for the Async Professional state machine components to be useful, their design
philosophy should be understood. The TApdStateMachine component contains and
manages the TApdState components. The TApdState components own a TCollection
descendent that determines the conditions required to progress to another TApdState. Each
TApdState component can have either a single or multiple conditions. When the
TApdStateMachine activates a state, the TApdStateMachine creates a list containing
TApdDataPacket components configured according to the properties of each condition.
Once one of those conditions is met, the next state is activated and the TApdStateMachine
awaits further input to satisfy the new state’s conditions.
The TApdStateMachine is the only state machine component that interfaces with the
TApdComPort or TApdWinsockPort. The TApdState components merely define the
conditions that the TApdStateMachine monitors.
States and conditions
A TApdState component contains a TCollection descendent (TApdStateConditions); which
contains TCollectionItem descendents (TApdStateCondition). The TApdStateCondition
class defines conditions for the TApdState.
A condition, in the realm of the state machine, consists of a StartString and an EndString, a
PacketSize, and a Timeout to determine the condition’s data requirement. The condition
also contains a pointer to the next state to activate when the data conditions are met; and an
ErrorCode to keep track of the relative success or failure of the state machine. The condition
also defines visual aspects (called a “Connectoid”) such as the caption, line color and line
width.
17
166 Chapter 6: State Machine Components
1
1
A TApdState component can be either activated or deactivated, and only activated or
deactivated by a TApdStateMachine. When a TApdState is activated, the TApdStateMachine
assembles the collection of conditions into a list of TApdDataPackets, and assigns internal
event handlers as appropriate. When the TApdDataPacket’s data match conditions, or
timeout, is met, the current state is deactivated and the next state is activated.
Adding, editing, and deleting conditions
1
2
3
The multiple-condition capability of the TApdState component does not lend itself very well
to programmatic modification, or modification through the Object Inspector. The
TApdState component installs a state condition editor dialog accessed by right-clicking the
component and selecting the “Edit condition...” menu item. A dialog box is displayed, as
shown in Figure 6.1, which provides access to add, edit and delete conditions.
4
5
6
7
8
9
10
Figure 6.1: TApdState component editor.
The grid displays the current values for the installed conditions. To add a new condition to
this TApdState component, click Add; to edit the selected condition click Edit; to delete the
selected condition click Delete. When the modifications are complete, click OK to update
the collection of conditions. To cancel any changes and revert to the original conditions,
click Cancel.
11
12
13
14
15
16
17
Chapter 6: State Machine Components 167
1
1
1
When a condition is added or edited, the Condition editor, shown in Figure 6.2, is displayed.
This dialog box permits editing the fields of the TApdStateCondition being added or edited.
2
3
4
5
6
7
8
9
Figure 6.2: TApdStateCondition property editor.
13
The controls are preinitialized with the current TApdStateCondition values when the
condition is being edited, and preinitialized with default values when the condition is being
added. Most of the controls are self-explanatory, with the exception of the “Next state” and
“Color” drop-down combo boxes. The drop-down list for “Next state” contains the names
of all TApdState components owned by the TApdStateMachine that owns the TApdState
being edited. The “Color” drop-down list contains the system colors and Delphi color
constants. Hex or RBG representations are not supported. Click OK to accept the changes or
click Cancel to cancel the changes. Note that the changes will not take effect until the
TApdState component editor is terminated.
14
The state machine in action
10
11
12
15
16
17
For illustration, we will create a simple state machine that will initialize a modem, initialize
the modem for Caller ID detection, collect the Caller ID information, and answer an
incoming call. This process can be broken up into four states: initialization, Caller ID
initialization, waiting for the ring signals from the modem, and answering the call. As you
will see, the multiple condition capability of the TApdState, and some carefully planned
recursion, will let us wait for the rings and detect the Caller ID information in a single
TApdState.
168 Chapter 6: State Machine Components
1
1
Due to the number of properties that are available when defining the state conditions, only
the properties that will be changed will be mentioned, other properties can be left at their
default values.
Create a new project and drop a TApdComPort component and a TApdStateMachine
component onto the new form. Next, drop four TApdState components onto the
TApdStateMachine.
Select ApdState1, change the OutputOnActivate property to “ATZ^M”. Right-click the
component and select the “Edit conditions...” menu. Add a new condition with a StartString
of “OK” and set the NextState to ApdState2. This will tell the TApdState that we want to send
“ATZ”<CR> when activated and we will wait for an “OK” before ApdState2 is activated.
Select ApdState2, change the OutputOnActivate property to “‘AT#CID=1’^M”. Invoke the
conditions property editor and add a new condition with a StartString to “OK” and the
NextState set to ApdState3. This will tell the ApdState2 that we want to send
“AT#CID=1”<CR> when the state is activated and wait for an “OK.”
Select ApdState3, and invoke the conditions property editor. In this state, we will wait for
two “RING” signals, and the three Caller ID tags. Invoke the conditions property editor and
add a new condition with StartString set to “RING.” Don’t set the NextState yet; it will be set
programmatically so the two “RING’s” can be captured. Add another condition and set the
StartString to “DATE:”, EndString to “^M” and NextState to ApdState3. Add two more
conditions identical to the last except for a StartString of “NAME:” and “NMBR:”. These
conditions will tell ApdState3 that we want to know when “RING” is received, and when the
Caller ID tags are received. Since we may or may not receive the Caller ID tags, and they may
be in a different order, their NextState properties will point to their own TApdState.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Chapter 6: State Machine Components 169
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
We will stay in this state until we tell the state that we want to progress to the next state. We
will also want to record the Caller ID information for later processing. To do this, create the
OnStateFinish event handler for ApdState3 and enter the following:
procedure TForm1.ApdState3StateFinish(State: TApdCustomState;
Condition: TApdStateCondition; var NextState: TApdCustomState);
begin
{ decide what to do when we receive "RING"s }
if Condition.StartString = 'RING' then begin
{ it's our RING condition }
State.Tag := State.Tag + 1;
if State.Tag > 1 then
{we've seen at least 2 rings, progress to the next state}
NextState := ApdState4
else
{ we've seen less than 2 rings, wait for more }
NextState := ApdState3;
end else if Condition.StartString = 'DATE:' then
{ it's our Date CID tag }
CIDDate := ApdStateMachine1.DataString
else if Condition.StartString = 'NMBR:' then
{ it's our Number CID tag }
CIDNumber := ApdStateMachine1.DataString
else if Condition.StartString = 'NAME:' then
{ it's our Name CID tag }
CIDName := ApdStateMachine1.DataString;
end;
Select ApdState4 and change the OutputOnActivate property to “ATA^M”. Invoke the
conditions property editor, add a new condition and change the StartString to
“CONNECT.”
Next, select the ApdStateMachine1 component on the form, set the StartState property to
ApdState1 and the TerminalState property to ApdState4. Add an OnStateMachineFinish
event handler to provide notification when the state machine is complete and we are
connected. A simple ShowMessage(“Connected”) will suffice for our purposes here.
Finally, drop the obligatory TButton on the form and create the OnClick event handler. This
event is where we will start the state machine. Add the following code to the OnClick event
handler:
procedure TForm1.Button1Click(Sender: TObject);
begin
ApdStateMachine1.Start;
end;
17
170 Chapter 6: State Machine Components
1
1
One implementation of the preceding example is illustrated in Figure 6.3.
1
2
3
4
5
6
7
8
9
10
11
12
13
Figure 6.3: State machine example.
Compile and run the project. Click the button to start the state machine. As the states are
activated they will be highlighted in yellow (change the TApdStateActiveColor property of
the TApdState component to change the default highlight color). The modem will first be
initialized with “ATZ”, after the “OK” is received the Caller ID initialization is sent. After OK
is received, ApdState3 will be highlighted waiting for incoming calls. When a call is detected
by the modem, ApdState3 will wait for two “RING”s, collecting whatever Caller ID
information is available. After two “RING”s, “ATA” is sent to the modem to answer the call
and we will wait for the “CONNECT” response from the modem.
14
15
16
17
Chapter 6: State Machine Components 171
1
1
1
TApdStateMachine Component
2
The TApdStateMachine component manages TApdState components to provide an easy to
use, easy to maintain, and easy to visualize state machine. The TApdStateMachine
component creates internal data collection mechanisms to monitor for conditions defined
by the owned TApdState components, and activates and deactivates the owned TApdState
components according to the collected data.
3
4
5
Hierarchy
TScrollingWinControl (VCL)
! TApdBaseScrollingWinControl (OOMisc) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
6
TApdCustomStateMachine (AdStMach)
TApdStateMachine (AdStMach)
7
Properties
8
9
10
11
12
ComPort
DataString
CurrentState
LastErrorCode
Data
StartState
DataSize
StateNames
Methods
Cancel
Events
OnStateChange
13
14
15
16
17
172 Chapter 6: State Machine Components
1
1
Start
OnStateMachineFinish
TerminalState
! Version
Reference Section
1
Cancel
method
2
procedure Cancel;
! Cancels the state machine.
3
Call the Cancel method to cancel the TApdStateMachine. The current state will be
deactivated, and the OnStateMachineFinish event will be generated. All resources allocated
by the TApdStateMachine to manage the current state will be deallocated.
4
When the Cancel method is used to terminate the TApdStateMachine, the ErrorCode
parameter of the OnStateMachineFinish event will be ecCancelRequested.
5
See also: OnStateMachineFinish
6
ComPort
property
7
property ComPort : TApdCustomComPort
! Determines the serial port used by the TApdStateMachine component.
A properly initialized TApdComPort or TApdWinsockPort must be assigned to this
property before starting the TApdStateMachine.
ComPort is usually set automatically at design time to the first TApdCustomComPort
component that the TApdStateMachine finds on the form. Use the Object Inspector to select
a different TApdCustomComPort component, if needed.
Setting the ComPort property at run time is necessary only when using a dynamically
created TApdCustomComPort or TApdStateMachine, or when selecting among several
TApdCustomComPort components.
To use this component through a TAPI interface, start the TApdStateMachine after the
OnTapiPortOpen event has been generated (after TAPI hands the application an initialized
and open serial port).
8
9
10
11
12
13
14
15
16
17
TApdStateMachine Component 173
1
1
1
CurrentState
read-only, run-time property
property CurrentState : TApdCustomState
2
! The TApdCustomState that is currently active.
3
During the execution of a state machine, different states will be activated and deactivated
depending on the data that is received. CurrentState is the TApdCustomState component
that is currently active.
4
At run time, the current state will be displayed with a highlighted background.
5
Data
read-only, run-time property
property Data : Pointer
6
! A pointer to the collected data.
7
As a TApdStateMachine manages an active TApdState component, internal
TApdDataPackets collect the data defined by the conditions. Data is identical to the Data
parameter of TApdDataPacket’s OnPacket event handler.
8
See also: DataSize, DataString
DataSize
read-only, run-time property
9
property DataSize : Integer
10
11
12
13
14
15
16
17
! The size of the collected data.
As the TApdStateMachine manages an active TApdState component, internal
TApdDataPackets collect the data defined by the conditions. DataSize is the size of the
collected data. DataSize is identical to the Size parameter of TApdDataPacket’s OnPacket
event handler.
See also: Data, DataString
DataString
property DataString : string
! The collected data.
As the TApdStateMachine manages an active TApdState component, internal
TApdDataPackets collect the data defined by the conditions. DataString is the string that
was collected. DataString is identical to the Data parameter of TApdDataPacket’s
OnStringPacket event handler.
See also: Data, DataSize
174 Chapter 6: State Machine Components
1
1
read-only, run-time property
LastErrorCode
read-only, run-time property
1
property LastErrorCode : Integer
! The ErrorCode of the last TApdStateCondition.
2
When a TApdState is deactivated, the condition whose data requirements were met defines
an ErrorCode value. ErrorCode is intended to provide a numerical result of the condition.
For example, if the TApdState conditions defined a log in sequence, and the failure
condition was met, ErrorCode could be set to TPS_LOGINFAIL (an Async Professional
predefined constant found in AdExcept.pas). This property can be used for status reporting,
or to provide a reason for a state machine failure. The TApdStateMachine ignores this value.
3
4
5
See also: OnStateMachineFinish
OnStateChange
event
6
property OnStateChange : TApdStateMachineStateChangeEvent
7
TApdStateMachineStateChangeEvent = procedure(
StateMachine : TApdCustomStateMachine;
FromState : TApdCustomState;
var ToState : TApdCustomState) of object;
8
! Defines an event handler that is called when the CurrentState changes.
When the TApdStateMachine progresses through the owned TApdState components the
TApdState components are deactivated and the next state is activated. The OnStateChange
event is generated immediately after the deactivation. The next state is activated
immediately after this event handler returns.
This event handler can be used to provide status indicators, or to modify the next state based
on the collected data. To specify a different TApdState, set the ToState parameter to the new
state to activate. To terminate the TApdStateMachine, set ToState to nil.
9
10
11
12
StateMachine is the TApdCustomStateMachine that generated the event. FromState is the
TApdCustomState component that was deactivated. ToState is the TApdCustomState that
StateMachine will activate.
13
See also: CurrentState, OnStateMachineFinish
14
15
16
17
TApdStateMachine Component 175
1
1
1
OnStateMachineFinish
event
property OnStateMachineFinish : TApdStateMachineFinishEvent
2
3
4
5
6
7
8
TApdStateMachineFinishEvent = procedure(
StateMachine : TApdCustomStateMachine;
ErrorCode : Integer) of object;
! Defines an event handler that is called when the state machine terminates.
When the TApdCustomState machine terminates, this event is generated to provide
notification of the termination. The TApdCustomStateMachine can terminate due to the
Cancel method, upon activation of the TerminalState TApdState, or when a condition is met
and the NextState property is nil or invalid.
StateMachine is the TApdCustomStateMachine that generated the event. ErrorCode is an
integer defining the reason for the termination. If the TApdCustomStateMachine was
terminated due to the Cancel method, ErrorCode will be ecCancelRequested. For all other
reasons, ErrorCode will be the value assigned to the ErrorCode property of the condition
whose data match parameters were met.
See also: Cancel, LastErrorCode
Start
9
10
11
method
procedure Start;
! Start starts the state machine.
The Start method starts the state machine. Once Start is called, the TApdState determined
by the StartState is activated. The StartState property must be assigned prior to calling Start.
See also: Cancel, StartState,
12
StartState
13
property StartState : TApdCustomState
14
15
! Determines the state that is activated first.
When the Start method is called, the TApdState component determined by StartState is
activated, which starts the state machine. If Start is called and StartState is not assigned, the
EStartStateNotAssigned exception will be raised.
See also: OnStateChange, Start
16
17
176 Chapter 6: State Machine Components
1
1
property
StateNames
read-only, run-time property
1
property StateNames : TStrings
! Provides the names of all TApdState components associated with the state machine.
2
The StateNames property is primarily for internal use, to provide the names of all TApdState
components that are associated with the TApdStateMachine. The Strings value is the Name
of the TApdState component.
3
TerminalState
4
property
property TerminalState : TApdCustomState
5
! Determines the state that terminates the state machine.
The TApdCustomStateMachine can terminate due to the Cancel method, upon activation of
the TerminalState TApdState, or when a condition is met and the NextState property is nil or
invalid. The TerminalState property determines which state is the terminal state. When this
state is activated, the OutputOnActivate property is transmitted through the serial port, and
any conditions defined by the TerminalState configured. When one of the conditions is met,
the TerminalState is deactivated and the OnStateMachineFinish event is generated.
6
7
8
See also: Cancel, OnStateMachineFinish
9
10
11
12
13
14
15
16
17
TApdStateMachine Component 177
1
1
1
TApdState Component
2
The TApdState component defines the conditions for which the state machine is monitoring
at a given time during execution of the state machine. The TApdState component does not
interact with the port, it simply contains the data requirements and state progression
elements of the state it is representing.
3
4
5
6
7
8
9
10
11
12
13
The TApdState component maintains a collection of state conditions, which means that a
single TApdState component can define several different conditions at one time. For
example, at the beginning of a ZModem file transfer, the transmitter can begin the transfer
by sending “rz”<CR>, or a ZInit block (“**[18]B00000000000000[0D]??”). This can be
represented in a single TApdState component by adding two conditions, with their
NextState properties pointing to the TApdState that responds with the ZRInit packet.
The conditions are contained in the Conditions property of TApdState, which is a
TCollection descendent. This property is a collection of TApdStateCondition classes, which
are descendents of TCollectionItem. The TApdStateCondition defines data match strings
and timeouts similar to the TApdDataPacket, as well as a TApdStateConnectoid which
defines the visual connectoid between the states. The TApdStateConditions,
TApdStateCondition and TApdStateConnectoid classes do not lend themselves well to
programmatic manipulation, they are better suited to changing from the Object Inspector
and property editors.
TApdStateCondition
The TApdState component maintains TApdStateConditions, which define the conditions to
satisfy the state. TApdStateCondition is a TCollectionItem descendent, and has the
properties shown in Table 6.2.
Table 6.2: TApdStateConditions
Name
Type
Description
StartString
String
Defines the beginning of the data
match string.
EndString
String
Defines the ending of the data match
string.
IgnoreCase
Boolean
Determines whether the data match
string must match case or not.
PacketSize
Integer
Defines packet length criteria for
the data match string.
14
15
16
17
178 Chapter 6: State Machine Components
1
1
Table 6.2: TApdStateConditions (continued)
1
Name
Type
Description
ErrorCode
Integer
Defines an error code when this
condition is satisfied.
2
NextState
TApdCustomState
Defines the state that is activated
once this condition is satisfied.
3
Connectoid
TApdStateConnectoid
Defines how the link from this state
to NextState is rendered.
StartString, EndString, IgnoreCase and PacketSize are very similar to the TApdDataPacket
properties of the same name. The interface is simplified somewhat with the omission of the
StartCond and EndCond properties. If StartString is an empty string the data collection
mechanism begins immediately once the state is activated. If StartString is not an empty
string the data collection mechanism begins when the StartString has been received. If
PacketSize is 0 the size of the collected data is ignored. If PacketSize is greater than 0 then
condition will be satisfied when PacketSize characters have been received. If EndString is an
empty string the condition is satisfied if either the StartString has been received or
PacketSize characters have been received. If EndString is not an empty string the condition
is satisfied when the EndString has been received after the data collection mechanism
begins.
4
5
6
7
8
9
Hierarchy
10
TGraphicContro l (VCL)
! TApdBaseGraphicControl (OOMisc) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
11
TApdCustomState (AdStMach)
TApdState (AdStMach)
12
13
14
15
16
17
TApdState Component 179
1
1
1
2
3
4
5
Properties
Active
Glyph
ActiveColor
GlyphCells
Conditions
InactiveColor
Methods
Terminate
Events
OnStateActivate
6
7
8
9
10
11
12
13
14
15
16
17
180 Chapter 6: State Machine Components
1
1
OnStateFinish
OutputOnActivate
! Version
Reference Section
Active
1
read-only, run-time property
2
property Active : Boolean
Default: False
!
3
Indicates whether this state is active or not.
As the TApdStateMachine component progresses through the TApdState components that
define the state machine, the TApdStateMachine component activates and deactivates the
TApdStates. This property indicates whether this TApdState is active or not.
When the TApdStateMachine component activates a TApdState, the conditions that define
the state are read from the collection maintained by the Conditions property of TApdState,
and corresponding TApdDataPackets are created. When the first condition is satisfied, the
TApdStateMachine deactivates the current state and activates the next TApdState. Active is
set after the data collection mechanism is configured, and after the data collection
mechanism is destroyed.
5
6
7
8
See also: OutputOnActivate
ActiveColor
4
property
9
property ActiveColor : TColor
10
Default: clYellow
! Determines the color of the component when the state is active.
When the TApdState is active, it will be rendered with a background color determined by
the ActiveColor property. When the state is inactive it will be rendered with a background
color determined by the InactiveColor property. If the Glyph property is assigned, that
image will be rendered instead with a background determined in part by GlyphCells.
11
12
13
See also: Active, Glyph, GlyphCells, InactiveColor
14
15
16
17
TApdState Component 181
1
1
1
Conditions
property
property Conditions : TApdStateConditions
2
3
4
5
6
TApdStateConditions = class(TCollection)
public
function Add : TApdStateCondition;
property Items[Index: Integer] : TApdStateCondition;
end;
! Defines the collection of conditions for the state.
This property is a collection of TApdStateCondition classes, which defines the data match
conditions for this TApdState component. The TApdStateConditions property defines a
property editor, which is the same as the component editor for this component. This design
time editor is discussed earlier in this chapter.
Glyph
7
8
9
property Glyph : TBitmap
! Defines a bitmap used to illustrate the state.
If the Glyph property is not assigned, the TApdState component is rendered as an empty
rectangle with a caption. If the Glyph property is assigned a valid bitmap, the component is
rendered with the bitmap.
10
The Glyph can contain multiple cells to display a different image for each stage of the
TApdState component. The GlyphCells property determines the number of cells contained
in Glyph. See GlyphCells for a more detailed description.
11
See also: GlyphCells
12
13
14
15
16
17
182 Chapter 6: State Machine Components
1
1
property
GlyphCells
property
1
property GlyphCells : Integer
2
Default: 1
! Determines the number of cells contained in the Glyph property.
The Glyph property can be unassigned, it can contain a single image that is displayed for the
duration of the state machine, or it can display a different image depending on the state of
the TApdState. GlyphCells determines how many images are contained in Glyph.
GlyphCells supports values from 0 through 3, and renders the cell according to the
following:
Glyph Cell
Condition When Rendered
0,1
When the TApdState is inactive.
2
When the TApdState is active.
3
After the TApdState has been deactivated.
3
4
5
6
For example, if Glyph contained a single cell that should be displayed for the duration of the
state machine, GlyphCells can be either 0 or 1. To display one image when the Active
property is False and another image when Active is True, GlyphCells should be 2 and the
Glyph should contain two images with the same dimensions placed side by side.
7
8
9
When the Glyph cell is rendered, the Caption property will be displayed above the image.
The Active, ActiveColor and InactiveColor properties determine the background color
behind the Caption.
10
See also: Active, ActiveColor, Glyph, InactiveColor
11
InactiveColor
property
12
property InactiveColor : TColor
13
Default: clWhite
! Determines the color of the component when the state is not active.
When the TApdState is active, it will be rendered with a background color determined by
the ActiveColor property. When the state is inactive it will be rendered with a background
color determined by the InactiveColor property. If the Glyph property is assigned, that
image will be rendered instead with a background determined in part by GlyphCells.
14
15
16
See also: Active, ActiveColor, Glyph, GlyphCells
17
TApdState Component 183
1
1
1
OnStateActivate
event
property OnStateActivate : TApdStateNotifyEvent
2
3
4
5
6
TApdStateNotifyEvent = procedure(
State : TApdCustomState) of object;
! Defines an event that is generated when the state is activated.
OnStateActivate provides notification when the TApdState component becomes active. This
event is generated when the TApdStateMachine component activates this TApdState
component. When this event is generated, the TApdStateMachine has already created and
configured the data collection mechanism defined by Conditions; and the string defined by
OutputOnActivate has already been transmitted. Use this event for notification and status
purposes only.
State is the TApdState component that generated the event.
See also: Activate, OutputOnActivate
7
OnStateFinish
8
property OnStateFinish : TApdStateFinishEvent
9
TApdStateFinishEvent = procedure(
State : TApdCustomState; Condition : TApdStateCondition;
var NextState : TApdCustomState) of object;
10
11
12
13
14
! Defines an event that is generated when the state’s conditions are satisfied.
OnStateFinish provides notification when a TApdState’s conditions have been met. This
event is generated immediately before this state is deactivated and the Condition’s NextState
is activated.
State is the TApdState component that generated the event. Condition is the
TApdStateCondition whose data match conditions were satisfied. NextState is the TApdState
component that will be activated next. NextState can be changed during this event to
provide a mechanism to select the next state based on collected data or other factors. If
NextState is nil the TApdStateMachine will terminate the state machine.
See also: Active, OnStateActivate
15
16
17
184 Chapter 6: State Machine Components
1
1
event
OutputOnActivate
property
1
property OutputOnActivate : string
! Defines a string to transmit when the state is activated.
2
As the TApdStateMachine progresses through the state machine, successive TApdState
components are activated and deactivated. The string defined by OutputOnActivate is
transmitted by the TApdStateMachine through the TApdCustomComPort determined by
TApdStateMachine’s ComPort property when the state is activated. OutputOnActivate
provides a convenient way of initiating the state.
For example, during a modem initialization state machine you may want to transmit
“ATZ”<CR> and wait for an “OK” or “ERROR” response from the modem. The collection
of conditions would contain a TApdStateCondition defining a StartString of “OK” and
another TApdStateCondition defining a StartString of “ERROR.” The OutputOnActivate
property could be set to “ATZ^M” at design time. When the state is activated, “ATZ”<CR>
will be transmitted and the response received by the same state’s conditions.
OutputOnActivate supports printable and non-printable characters in the string. At design
time, enter non-printable characters in caret or pound notation. For example, to add a
<CR> character, the “^M” or “#13” sequence (without quotes) can be used. At run time,
the literal character can be used.
4
5
6
7
8
9
See also: Active, OnStateActivate
Terminate
3
method
10
procedure Terminate(ErrorCode : Integer)
11
! Terminates the TApdState component.
Under normal circumstances, the data match conditions defined by the Conditions
property of TApdState would cause the TApdStateMachine to deactivate the state. The
Terminate method can be used to stop the data collection mechanism. When the Terminate
method is called, the TApdStateMachine will deactivate the state (causing the OnStateFinish
event to be generated), and the OnStateMachineFinish event of TApdStateMachine will be
generated. The ErrorCode parameter of this method will be passed along to the ErrorCode
parameter of the OnStateMachineFinish event handler.
See also: OnStateFinish
12
13
14
15
16
17
TApdState Component 185
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
186 Chapter 6: State Machine Components
1
1
Chapter 7: Status Light Components
1
2
The components included in this chapter: TApdStatusLight and TApdSLController, allow
you to add a status light display, similar to the LEDs found on external modems, to your
communications programs. The display can give a visual indication of modem status lights
such as RTS and RI.
3
TApdStatusLight is a simple component that displays two bitmaps, or two different colors,
depending on whether the light is “lit” or “unlit.” The component’s Lit property determines
which of the two states is displayed, and its Glyph property determines whether bitmaps or
solid colors are used. This component works hand-in-hand with the TApdSLController
component.
4
The TApdSLController component monitors the status of a TApdComPort component and
changes the state of one or more TApdStatusLight components to reflect that status.
6
5
7
8
9
10
11
12
13
14
15
16
17
187
1
1
1
TApdStatusLight Component
2
TApdStatusLight is a simple component that displays two bitmaps, or two different colors,
depending on whether the light is “lit” or “unlit.” The component’s Lit property determines
which of the two states is displayed, and its Glyph property determines whether bitmaps or
solid colors are used.
3
4
5
6
This component works hand-in-hand with the TApdSLController component (see page
191). TApdSLController reacts to changes in serial port status and changes the Lit property
of various TApdStatusLight components to reflect the status of the port.
Hierarchy
TGraphicControl (VCL)
TApdCustomStatusLight (AdStatLt)
7
8
9
TApdStatusLight (AdStatLt)
Properties
Glyph
LitColor
Lit
NotLitColor
10
11
12
13
14
15
16
17
188 Chapter 7: Status Light Components
1
1
Reference Section
Glyph
1
property
2
property Glyph : TBitmap
! Determines two custom bitmaps used to display the status light.
3
If the Glyph property is not assigned, a “lit” status light is drawn as a solid red square and an
“unlit” one as a green square. Each square has shadowed edges to give it a three dimensional
appearance.
Glyph can be used to display custom bitmaps instead. The Glyph bitmap is actually two
bitmaps in one. It should be twice as wide as the component’s Width property. The bitmap
displayed when the status light is lit is Width pixels wide starting at the left edge of the
bitmap. The bitmap displayed when the status light is not lit is Width pixels wide starting
Width pixels from the left edge of the bitmap.
Lit
property
property Lit : Boolean
4
6
7
8
Default: False
! Determines the state in which the status light is drawn.
9
This property is normally assigned by the trigger handlers of the TApdSLController
component.
LitColor
property
10
11
property LitColor : TColor
12
Default: clRed
! Determines the color of a no-glyph status light in its lit state.
If the component’s Glyph property is not set to a valid bitmap, the status light is drawn as a
slightly raised colored square. LitColor is the color in which that square is drawn when the
light is lit.
13
14
15
16
17
TApdStatusLight Component 189
1
1
1
NotLitColor
property
property NotLitColor : TColor
2
Default: clGreen
! Determines the color of a no-glyph status light in its unlit state.
3
4
If the component’s Glyph property is not set to a valid bitmap, the status light is drawn as a
slightly raised colored square. NotLitColor is the color in which that square is drawn when
the light is not lit.
5
6
7
8
9
10
11
12
13
14
15
16
17
190 Chapter 7: Status Light Components
1
1
TApdSLController Component
1
The TApdSLController component monitors the status of a TApdComPort component and
changes the state of one or more TApdStatusLight components to reflect that status. The
goal of the component is to give communications programs a status light display similar to
the LEDs found on external modems.
2
TApdSLController is capable of monitoring the port’s line signals (DCD, DTR, CTS, and
RI), line breaks and errors, and whether data is currently being received or transmitted.
3
4
The Lights property
The controller has a property called Lights which holds pointers to the status light
components that the controller will be monitoring. Lights is of type TLightSet which is
simply a class that contains a property of type TApdCustomStatusLight for each line
condition that TApdSLController can monitor. In the Object Inspector, the Lights property
has eight sub-properties that are used to assign status lights to the line conditions you want
to monitor.
6
7
Table 7.1 provides a list of all Lights sub-properties and the port condition they monitor.
8
Table 7.1: Lights property sub-properties and port conditions
9
Sub-property
Line condition
BREAKLight
Lit for BreakOffTimeout ticks when a line break occurs.
CTSLight
Lit when CTS signal high.
DCDLight
Lit when DCD signal high.
DSRLight
Lit when DSR signal high.
ERRORLight
Lit for ErrorOffTimeout ticks when a line error occurs.
RINGLight
Lit for RingOffTimeout ticks after RI signal goes high.
RXDLight
Lit when data is being received.
TXDLight
Lit when data is being transmitted.
10
11
12
13
14
Using a TApdSLController
To use a TApdSLController, first create a TApdStatusLight component for each line
condition you want the controller to monitor. Next, drop a TApdSLController component
on the form and link it to the TApdComPort component you wish to monitor. Next, link the
controller’s light properties to the status light components. Set the controller’s Monitoring
property to True at run time when you want to start monitoring.
15
16
17
TApdSLController Component 191
1
1
1
Figure 7.1 shows the Object Inspector for a properly created TApdSLController component
that can be used for monitoring the CTS, DCD, and DSR line signals.
2
3
4
5
6
7
8
9
Figure 7.1: Viewing the Object Inspector of a properly created TApdSLController component.
10
When the CTS signal changes, the component named CTSMonitor is changed accordingly.
Similarly, the component named DSRMonitor changes when the DSR signal changes and
the DCDMonitor component changes when the DCD signal changes.
11
Hierarchy
12
TComponent (VCL)
TApdCustomSLController (AdStatLt)
13
14
15
TApdSLController (AdStatLt)
Properties
BreakOffTimeout
Lights
RXDOffTimeout
ComPort
Monitoring
TXDOffTimeout
ErrorOffTimeout
RingOffTimeout
16
17
192 Chapter 7: Status Light Components
1
1
Reference Section
BreakOffTimeout
1
property
2
property BreakOffTimeout : LongInt
Default: 36
!
3
Determines the number of ticks the BREAKLight remains lit after a line break is detected.
ComPort
property
4
property ComPort : TApdCustomComPort
! Determines the serial port monitored by the status lights.
The status light controller monitors the status of lines on a single serial port. This property
must be linked to the TApdComPort component that will be monitored.
ErrorOffTimeout
property
property ErrorOffTimeout : LongInt
6
7
8
Default: 36
! Determines the number of ticks the ERRORLight remains lit after a line error is detected.
Lights
property
9
10
property Lights : TLightSet
11
! Determines all of the lights displayed in the status bar.
Lights is simply a class that contains properties for each port condition that
TApdSLController can monitor. In the Object Inspector, these individual light properties
show up as sub-properties. That is, the inspector shows a property called Lights which will
expand into a list of individual properties when double-clicked.
12
13
For more information, see “The Lights property” on page 191.
14
15
16
17
TApdSLController Component 193
1
1
1
Monitoring
run-time property
property Monitoring : Boolean
2
3
! Determines whether the status lights are being updated.
Setting Monitoring to True causes the status light controller to install various triggers that
are used to update the status lights. Setting it to False removes those triggers.
ComPort must be assigned before setting Monitoring to True at run time.
4
5
RingOffTimeout
property
property RingOffTimeout : LongInt
Default: 8
6
7
! Determines the number of ticks the RINGLight remains lit after a ring is detected.
RXDOffTimeout
property
property RXDOffTimeout : LongInt
8
9
Default: 1
! Determines the number of ticks the RXDLight remains lit after a character is received.
TXDOffTimeout
10
11
property TXDOffTimeout : LongInt
Default: 1
! Determines the number of ticks the TXDLight remains lit after a character is transmitted.
12
13
14
15
16
17
194 Chapter 7: Status Light Components
1
1
property
Chapter 8: The Terminal Components
1
2
Traditionally, a terminal is a piece of hardware with a screen and keyboard that provides a
method to display information from a host computer and to enter information into the same
computer. Today, more often than not, terminals are personal computers that run a program
that emulates the original terminal; it interprets the same data in the same fashion and
displays it in the same way. Similarly, it emulates the original keyboard and sends the same
kind of information back to the host computer in the same format.
3
4
The data presented by the host computer takes one of two forms. The first form is intended
for display only. The terminal will just send the stream of data directly to the screen without
trying to interpret it in any fashion. What you see is what is sent. This is known as teletype
mode (abbreviated as TTY).
5
6
The other form of data sent to a terminal consists of two types, intermixed: displayable data
and embedded terminal control sequences. The Terminal control sequences cause the
terminal to move the cursor around the screen, to block off certain parts of the display from
being altered, to delete text from areas of the screen, to scroll the display, to switch character
sets, and so on. The terminal has to monitor the data being sent by the host computer, be
able to identify the terminal control sequences in the stream, and then extract and act on
them. All other data would be sent to the screen as usual. We usually refer to this mode as the
terminal emulation mode.
7
8
9
All emulation means is that the PC is pretending to be the terminal in such a fashion that the
host computer is not aware that there is no real terminal present, just a clever program.
Ideally, the PC program emulates the terminal so well that the user cannot tell the difference
between the program’s window and the original display. What you see is what was intended.
Unfortunately, the same cannot be said of the keyboard; terminal keyboards generally have
extra keys not available on a PC keyboard, and so the emulation program has to map
available PC keystrokes to the original terminal keyboard keys, and obviously the user has to
be aware of these mappings.
10
11
12
13
There are a variety of standards for terminal control sequences from such companies as
IBM, Digital Corporation (DEC), Wyse, and so on. One of the most widely known is the
DEC VT100 standard, which helped form the basis for the ANSI standard. Async
Professional implements the full VT100 standard, including support for different character
sets, scrolling regions, and keyboard application modes. By definition, this is also a subset of
the ANSI standard. Also, since the vast majority of PCs running Windows have color
monitors, the Async Professional VT100 emulation also supports the terminal control
sequences needed to display text in color.
14
15
16
17
195
1
1
1
Terminal Design Considerations
2
Async Professional provides terminal emulation capabilities with its terminal component,
TAdTerminal and its emulators, all descended from TAdTerminalEmulator. These
components have been designed with two goals in mind: first, to be usable with the
minimum of effort on your part; and, second, to be flexible so that you can extend them to
work with other terminal types.
3
4
5
6
7
8
9
10
11
The former goal is important to those programmers who know that having a terminal that is
compliant with the VT100 standard (or a subset of the ANSI standard) is all that is required
for their application. They do not want to worry about linking this kind of display emulation
with that kind of keyboard emulation to the terminal component, they would rather just
point the emulator at the terminal component and let the components do the rest.
Catering for the latter goal, extensibility, is more difficult. There are many facets to
emulating a terminal. You have to worry about how to store the data from the host; in other
words, the actual displayable characters, the character attributes, the colors (both
foreground and background), the character sets. You have to worry about interpreting the
terminal control sequences being sent by the host and acting upon them. These terminal
control sequences may erase parts of the display, insert extra lines, or scroll the text in
different ways. You have to track these changes in your data.
Once you have those kind of problems nailed down, you then have to consider the keyboard.
Ideally, you would like to emulate the original terminal keyboard completely, but this isn’t
generally possible. Apart from the alphabetic part of the keyboard, terminal keyboards have
different keys. You must map some PC keys (maybe normal, Control or Alt shifted) onto
their terminal keyboard equivalents. You have to consider what control sequence the
terminal keyboard would send to the host when a given key was pressed.
12
13
14
15
16
17
196 Chapter 8: The Terminal Components
1
1
After all that, you have to actually draw the terminal display in a normal window on the
screen. With the terminal classes in Async Professional, the attempt has been made to
simplify this entire process (see Figure 8.1).
1
2
SERIAL
DEVICE
3
4
COMPORT
BUFFER
TERMINAL
6
EMULATOR
KEYBOARD
MAPPING
7
PARSER
CHARACTER
SET
MAPPING
8
9
Figure 8.1: Terminal design example diagram.
10
Terminal buffer
First, there is the TAdTerminalBuffer class. This class provides a non-visual buffer
containing a representation of the data being displayed by the terminal window. The buffer
is organized in the same fashion as the terminal display: a matrix of data organized into rows
and columns. In fact, the buffer maintains a set of such matrices: one for the displayable
characters, one for the attributes (bold, underline, invisible, and so on), one for the color of
the text, one for the color of the background, and finally one for the character set identifiers.
There is a set of methods for performing the standard terminal operations: erasing or
deleting part of the display, inserting rows and characters, scrolling areas of the display,
changing the default colors and attributes, writing new characters to the screen, and so on.
The class also maintains a list of character positions and rectangles on the screen that need
repainting. This class does not of itself understand any terminal control sequences; its
functionality is driven by some controlling terminal emulation component. In writing a new
terminal emulation component, you would generally use this buffer class as is; the only time
you would create your own descendant would be if the terminal you are emulating requires
some transformation of the display not provided by the standard class.
11
12
13
14
15
16
17
Terminal Design Considerations 197
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Terminal parser
Next, there is the TAdTerminalParser class. This class is designed to interpret terminal
control sequences. The TAdTerminalParser class is an ancestor class, it merely defines the
interface through which a parser should work. Async Professional provides a single
descendant, the TAdVT100Parser class, which provides parsing capabilities for the VT100
series of terminals. Studying the code for this class would provide hints on how to write a
specialized parser class descendant for another terminal.
Keyboard-mapping table
Next, there is the keyboard-mapping table. This class stores the information required to
map a PC keystroke (as reported by Windows) into the control sequence that has to be sent
to the terminal’s host computer. This process takes place in three distinct steps, the
information for each being stored in the same structure (actually, a hash table). First, the
virtual key code reported by Windows is looked up in the table. If found, the name of the
virtual key would be returned. This name, together with the state of the various shift keys
(including Ctrl and Alt), is then looked up in the same table. If found, it will return the name
of the terminal key to which the PC key maps. For example, with the standard VT100
mappings provided with Async Professional, the up arrow key on the PC keyboard, VK_UP,
is mapped to DEC_UP, the name for the up arrow on the VT100 keyboard. Finally, the
terminal key name is looked up in the same table in order to obtain the control sequence that
needs to be sent to the host computer.
This seemingly repetitive triple lookup process exists to enhance comprehension and
extensibility. Consider: the first and last lookups are fully determined by the Windows
operating system and the terminal definition. They should not be altered. Virtual key code
$26 is VK_UP, by definition, and the VT100 up arrow key will send either <Esc>[A or
<Esc>OA depending on the terminal mode, again by definition. Thus, you could specify
that virtual key code $26 will send either <Esc>[A or <Esc>OA, but it wouldn’t help with
understanding the mapping later on; whereas a mapping of VK_UP to DEC_UP is
immediately understandable and is easily changed. For example, if you wanted to make F6
the same as the up arrow key, you could just add the mapping of VK_F6 to DEC_UP. You
don’t have to manually look up the virtual key code for F6, nor the escape sequence
generated by the VT100 up arrow key to write the mapping.
For convenience, the keyboard-mapping table can be loaded in two ways. First, the
mappings can be read from a simply formatted text file. Second, the mappings can be stored
in a resource linked into your application, from which the class can easily load them. The
latter method has the advantage that no external files are required to define keyboard
mappings. To aid in the generation of the resource file, the class has a method by which a
binary file containing the mappings can be written. This binary file can then be compiled
into a resource and linked into your application.
198 Chapter 8: The Terminal Components
1
1
Character set mapping table
1
Next there is the character set mapping table. This class stores the information required to
actually display a given character on the terminal display. This class simplifies the task of
creating one terminal that uses several different character sets for displaying text. For each
character set, there may be a different glyph for each character. The classic example is that of
the VT100 terminal. This terminal has several character sets, of which two are the most
commonly used: the USASCII character set and the special graphics character set. To take as
an example, the character ‘m’ is displayed as a lower-case m in the USASCII character set,
but is displayed as a line draw lower left corner (the glyph that looks like an L) in the special
graphics character set.
The character set mapping table is a list of character ranges in character sets and the fonts
and glyphs that should be used to display them. To aid in the display of text on the terminal
keyboard—and to make the process more efficient—the character set mapping table will
analyze a string to be displayed in a particular character set, and generate a script of font
changes and the strings to be displayed in those fonts.
For convenience, the character set mapping table can be loaded in two ways. First, the
mappings can be read from a simply formatted text file. Second, the mappings can be stored
in a resource linked into your application, from which the class can easily load them. The
latter method has the advantage that no external files are required to define keyboard
mappings. To aid in the generation of the resource file, the class has a method by which a
binary file containing the mappings can be written. This binary file can then be compiled
into a resource and linked into your application.
2
3
4
6
7
8
9
10
Terminal emulator
The final step in writing your own terminal is possibly the most intricate: writing a terminal
emulator component. This component will have its own buffer instance, it will have a parser
instance and it will use a keyboard-mapping table and a character set mapping table. The
emulator would write to a TAdTerminal window and would be passed incoming data and
keyboard data from that component. The sequence of events for displaying something on
the screen would go something like this:
1. Accept a character from the input data stream.
11
12
13
14
2. Pass the character to the parser. The parser would decide whether the character was
displayable or part of a control sequence (a command) and pass the result back.
3. If the parser indicated that the character was displayable, the component would pass
the character to the buffer (equivalent to writing it to the display).
15
16
17
Terminal Design Considerations 199
1
1
1
2
3
4
5
4. If the parser indicated that the character was a command, the terminal component
would act on the command. This may be as simple as sending something back to the
server or as complex as scrolling the display. The latter operation would be passed to
the buffer to do.
5. Every now and then, the terminal component would get a paint message, at which
point it would have to interrogate the buffer to find the parts of the display that need
repainting, and draw them in its client area.
As far as the keyboard goes, the terminal component would convert the virtual key codes
into control sequences in the manner already described and send these sequences to the
server. For the majority of the keys on the keyboard, the letter keys, no translation is
required and the relevant character is sent as is.
6
7
8
9
10
11
12
13
14
15
16
17
200 Chapter 8: The Terminal Components
1
1
TAdTerminalBuffer Component
1
The TAdTerminalBuffer class defines a data structure for maintaining the data required for a
communications terminal display. Essentially, this consists of:
2
• The characters that should be shown.
3
• The character sets from which those character glyphs are drawn.
• The color in which the character glyphs will be displayed.
4
• The color for the background behind the characters.
• A set of display attributes for the characters.
It is important to realize that the terminal buffer class is only a data structure; it has no
responsibilities for the display of the data it stores. That work is delegated to the terminal
and emulator components. In essence, an emulator component creates an instance of a
terminal buffer to store the data that it will display on the screen. The emulator component
will call methods of the terminal buffer to perform such tasks as writing characters,
inserting and deleting lines, scrolling the data, changing colors and attributes, and the like.
These tasks will, in theory, be performed in response to control sequences being received by
the terminal from some remote program or host and being decoded by an emulator. Hence
the methods of the terminal buffer class mimic the standard operations performed by all
character-based terminals, but in particular by the DEC VT100 terminals.
Conceptually, the terminal buffer class manages five different screens worth of information
as independent matrices. These matrices are representations of the terminal screen and
hence are divided into rows and columns. One of the matrices holds the characters that
should be displayed on the screen. Another, holds the foreground color (i.e., the color of the
text), and yet another, holds the background color. A fourth holds the character set from
which the characters are drawn. The final matrix holds the special display characteristics for
the text, whether it is bold, underlined, blinking and so on. Using this scheme it is therefore
possible for every addressable character cell on the terminal display to be a different
character, from different character sets, using different colors and attributes. The character
matrix stores either a single byte per character cell (ANSI mode) or two bytes per character
(UNICODE mode), the choice being made when the terminal buffer is created. The
identifier for the character set is assumed to be limited to one of 256 possible values, and
hence just a single byte is stored per character cell to hold this information. The colors are
assumed to be stored as RGB values, and hence each character cell has a 4-byte long integer
color value for both the foreground color and the background color. (Notice that the
terminal buffer does not enforce the rule that the colors have to be RGB values, you could
store standard values from the 4-bit color set instead if you wished—the terminal buffer just
6
7
8
9
10
11
12
13
14
15
16
17
TAdTerminalBuffer Component 201
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
assumes that a color is a 4-byte quantity.) The attributes are stored as a set of different
possibilities: bold, underlined, strikethrough, blinking, reverse or invisible.
A word is required here regarding character sets. The VT100 terminal was a 7 bit device. In
theory, characters could have ASCII values from 0 to 127; however, the first 32 characters in
this set were control characters rather than displayable characters. Hence, there were only 96
different characters that could be displayed. Since this was not sufficient, the VT100 had
other sets of 96 characters that could be switched in and out, the most distinctive being the
line-draw characters in the special graphics character set. To display a character at a
particular position on the screen the terminal had to know which character set to take the
displayed glyph from, and also the ASCII value of the character. (A glyph is the visual
representation of an ASCII value in a particular character set.) Thus, on a VT100, the ASCII
value 123 would be displayed as the left curly brace, {, if the standard character set was in
force, or the symbol for pi, π, if the special graphics character set was in force.
Another point should to be made about the terminal buffer: it does not parse any data
stream for control sequences. This is the job of the emulator portion of a terminal
component. The emulator should scan the incoming data stream for control sequences (for
example, those beginning with “<Esc>[”), decode them, and then get the terminal buffer to
update itself according to the operation requested.
Apart from the data to be displayed, a terminal component requires two basic pieces of
information: Has the cursor moved? What needs to be redisplayed? The terminal buffer
maintains a cursor position variable: the row and column number where new characters are
to be written should they arrive. Various methods update the cursor position, the most
obvious ones being the cursor movement methods to move the cursor up, down, left or
right. Again, it should be stressed that there is no visual representation of a cursor in a
terminal buffer object: the cursor is just a pair of row and column values. It is up to the
terminal component displaying the data from the buffer to maintain and show a cursor, for
example, perhaps a blinking underscore or block. (By the way, this can get confusing: with
terminals the blinking point where editing takes place is called a cursor, whereas with
Windows programs, it is known as a caret, the cursor being the mouse pointer.) The
terminal buffer not only maintains a cursor position, it also interfaces a routine that returns
whether the cursor position has changed since the last time the routine was called.
If the terminal component is to have a chance of keeping up with fast data streams, it cannot
continually redisplay the entire terminal screen every now and then. It must have a way of
knowing what has changed on the terminal screen, so that it only needs to update that
particular section. For example, if a character was written to the terminal, the terminal
component needs to know how much of its window it needs to repaint. In the majority of
cases, that’s a single character cell; the new character, in other words. In some cases, the
terminal data needs to be scrolled before the character can be written. In this case the
terminal display needs to repaint a lot more of the window to show the effect of writing the
character. The terminal buffer maintains an internal list of invalidated character cells (in
202 Chapter 8: The Terminal Components
1
1
fact, as cell rectangles) and the terminal component can read these and use the information
to redisplay parts of its window.
The terminal buffer has two views of the data: the scrollback view and the display view. The
scrollback view shows a history of data that have scrolled off the top of the display view. The
display view is essentially the representation of the terminal screen itself. There are typically
more rows in the scrollback view than in the display view (otherwise there wouldn’t be
anything to scroll back through). The number of columns in both the scrollback and display
views is however the same.
The user of the terminal buffer will refer to positions on the terminal screen as one-based
values. For example, on a 24 rows x 80 columns terminal, the rows are numbered from 1 to
24 and the columns from 1 to 80. This is different from the usual Windows way of looking at
things, where values are counted from 0 instead. The rows in the scrollback view are negative
numbers or zero. For example, the last row to be scrolled off the display view (i.e., the
terminal itself) will be numbered row 0, the next to last, row –1, and so on. If a 24 row
terminal screen is cleared or erased, the entire scrollback view is scrolled up with the
previous display, and the rows of this previous display would then be numbered –23 to 0.
The terminal buffer also supports the concept of a scrolling region. This is a region of the
terminal screen to which the cursor is restricted. Once a scrolling region is defined and
activated, all cursor movement is restricted to this area and characters can only be written to
this area. When a scrolling region is activated, row and column numbers are no longer
absolute values, counted from the top left character cell of the screen, but are now relative
values, counted from the top left character cell of the scrolling region. For example, suppose
a scrolling region is defined to be within rows 5 and 10 and it is activated. Moving the cursor
to row 1, column 1, results in it being moved to the home position of the scrolling region,
not the home position of the screen. In absolute terms, the cursor ends up at row 5, column
1 instead. The terminal buffer maintains a definition of a scrolling region and also a flag that
states whether the scrolling region is in force or not. If a new scrolling region is defined, the
activation flag is automatically set, in other words, the scrolling region becomes effective
immediately.
Generally, when a data stream is sent to a terminal, the sender does not send coloring or
attribute information with every single character that needs to be displayed. Instead, the
terminal is instructed to use a particular color or attribute from that point forward, until
another color or attribute is requested. Characters written to the terminal in between these
two instructions will automatically be in the requested color or have the requested attribute.
The terminal also has a reset state, with default colors and attributes. The terminal buffer
mimics this behavior by having both current values and default values for the colors,
attribute and character set. If the terminal is reset, the terminal buffer automatically sets the
current values equal to the default values.
1
2
3
4
6
7
8
9
10
11
12
13
14
15
16
17
TAdTerminalBuffer Component 203
1
1
1
Hierarchy
TObject (VCL)
2
3
4
5
6
7
8
9
10
11
12
13
14
TAdTerminalBuffer (ADTrmBuf)
Properties
BackColor
DefForeColor
SVRowCount
Charset
ForeColor
UseAutoWrap
Col
OriginCol
UseNewLineMode
ColCount
OriginRow
UseScrollRegion
DefBackColor
Row
UseWideChars
DefCharset
RowCount
Methods
ClearAllHorzTabStops
EraseFromBOL
InsertLines
ClearAllVertTabStops
EraseLine
MoveCursorDown
ClearHorzTabStop
EraseScreen
MoveCursorLeft
ClearVertTabStop
EraseToEOL
MoveCursorRight
Create
EraseToEOW
MoveCursorUp
DeleteChars
GetCharAttrs
Reset
DeleteLines
GetDefCharAttrs
SetCharAttrs
DoBackHorzTab
GetInvalidRect
SetCursorPosition
DoBackspace
GetLineAttrPtr
SetDefCharAttrs
DoBackVertTab
GetLineBackColorPtr
SetHorzTabStop
DoCarriageReturn
GetLineCharPtr
SetScrollRegion
DoHorzTab
GetLineCharSetPtr
SetVertTabStop
DoLineFeed
GetLineForeColorPtr
WriteChar
DoVertTab
HasCursorMoved
WriteString
EraseChars
HasDisplayChanged
EraseFromBOW
InsertChars
15
16
17
204 Chapter 8: The Terminal Components
1
1
Reference Section
BackColor
1
property
2
property BackColor : TColor
Default: clBlack
!
3
Defines the background color for succeeding text.
The BackColor property determines the background color that will appear behind text
displayed in the terminal. Once the background color has been set, any new text written to
the terminal will appear with this color until the background color is set to another value.
4
See also: DefBackColor, DefForeColor, ForeColor
CharSet
property
property CharSet : Byte
6
7
Default: 0
8
! Defines the character set from which characters are drawn.
The Charset property determines the character set from which a character will be drawn.
Once a new character set is set, all characters written to the display should be displayed with
glyphs from this character set.
The terminal buffer class imposes no structure or valid values to a character set. It is up to
the terminal emulator component to impose meaning to the different character set values
(e.g., by assuming the value 0 means “default character set”).
9
10
11
See also: DefCharset
ClearAllHorzTabStops
method
procedure ClearAllHorzTabStops;
12
13
! Removes all horizontal tab stops.
The terminal buffer does not have any default tab stops (for example, a tab stop every 8
characters).
14
See also: ClearHorzTabStop, SetHorzTabStop
15
16
17
TAdTerminalBuffer Component 205
1
1
1
ClearAllVertTabStops
method
procedure ClearAllVertTabStops;
2
! Removes all vertical tab stops.
The terminal buffer does not have any default vertical tab stops.
3
See also: ClearVertTabStop, SetVertTabStop
4
ClearHorzTabStop
method
procedure ClearHorzTabStop;
5
6
! Clears a horizontal tab stop at the current cursor column.
If no horizontal tab stop is set for this column, ClearHorzTabStop does nothing.
See also: ClearAllHorzTabStop, SetHorzTabStops
7
ClearVertTabStop
8
procedure ClearVertTabStop;
9
method
! Clears a vertical tab stop at the current cursor row.
If no vertical tab stop is set for this row, ClearVertTabStop does nothing.
See also: ClearAllVertTabStops, SetVertTabStop
10
11
Col
property
property Col : Integer
! Defines the column of the cursor.
12
The Col property refers to the cursor. Reading the Col property returns the column number
of the cursor, and setting it moves the cursor to that column in the current row.
13
The value used for the Col property is one-based; in other words, columns are counted from
1. If there are 80 columns across the terminal screen, the columns will be known as columns
1 to 80. If the column number used is out of the range of the screen or the current scrolling
region, it is forced silently into bounds.
14
15
If a scrolling region is activated, the values for Col will be relative to the home position of the
scrolling region, not the home position of the screen itself.
16
See also: Col, ColCount, SetCursorPosition, UseScrollingRegion
17
206 Chapter 8: The Terminal Components
1
1
ColCount
property
1
property ColCount : Integer
2
Default: 80
! Defines the number of columns across the terminal screen.
The ColCount property is the number of columns displayed by the terminal screen. For the
VT100 terminal, for example, there are two possible values: 80 and 132.
If the ColCount property is changed, it is checked to be at least 2; otherwise, an exception is
raised.
Changing the ColCount property for an existing terminal screen does not clear the data
being displayed by the screen; you will need to do this as a separate step. The positions of
any horizontal tab stops are maintained, except for those that lie outside the new value for
ColCount. However, any scrolling region is discarded, and the cursor is reset to the home
position of the screen.
See also: Col, RowCount
Create
method
constructor Create(aUseWideChars : Boolean);
3
4
6
7
8
9
! Creates an instance of the TAdTerminalBuffer class.
The aUseWideChars parameter, if True, determines whether the Terminal is configured to
work with UNICODE characters, and allocates the underlying character matrix accordingly.
10
DefBackColor
11
property
property DefBackColor : TColor
12
Default: clBlack
! Defines the default background color.
13
If Reset is called, the current background color is set to the value of DefBackColor.
14
See also: BackColor, DefForeColor, Reset
15
16
17
TAdTerminalBuffer Component 207
1
1
1
DefCharset
property
property DefCharset : Byte
2
Default: 0
! Defines the default character set value.
3
4
If Reset is called, the current character set is set to the value of DefCharset.
See also: Charset, Reset
DefForeColor
5
6
7
property
property DefForeColor : TColor
Default: clSilver
! Defines the default color to be used for displaying text.
If Reset is called, the current foreground color is set to the value of DefForeColor.
See also: DefBackColor, ForeColor, Reset
8
9
DeleteChars
method
procedure DeleteChars(aCount : Integer);
! Deletes characters from the current cursor position.
10
11
The characters to the right of the deleted ones are moved over to take their place. The area
on the extreme right uncovered by this action is filled with space characters using the
current colors and attributes.
The DeleteChars method is limited to the current scrolling region.
12
The cursor is left in the same position.
13
DeleteLines
method
procedure DeleteLines(aCount : Integer);
14
! Deletes lines from the current cursor position.
15
The lines underneath the lines being deleted are moved up to take their place. The area at the
bottom uncovered by this action is filled with space characters using the current colors and
attributes. Deleting aCount lines is equivalent to scrolling up aCount lines.
16
The DeleteLines method is limited to the current scrolling region.
17
The cursor is left in the same position.
208 Chapter 8: The Terminal Components
1
1
DoBackHorzTab
method
1
procedure DoBackHorzTab;
! Moves the cursor left to the previous tab stop.
2
If there is no previous tab stop, the cursor is moved to the first column of the line. If it
already at this position, it is not moved.
3
This method is limited to the current scrolling region.
4
See also: DoHorzTab, SetHorzTabStop
DoBackspace
method
procedure DoBackspace;
6
! Backspaces the cursor.
The character underneath the new position of the cursor is not erased by this operation. A
backspace is equivalent to moving the cursor one position to the left, except that, if the
cursor is at the beginning of the row, it is not moved at all.
7
This method is limited to the current scrolling region.
8
See also: MoveCursorLeft
DoBackVertTab
9
method
10
procedure DoBackVertTab;
! Moves the cursor up to the previous tab stop.
If there is no previous tab stop, the cursor is moved to the first row. If it already at this
position, it is not moved.
This method is limited to the current scrolling region.
See also: DoVertTab, SetVertTabStop
DoCarriageReturn
11
12
13
method
14
procedure DoCarriageReturn;
! Moves the cursor to the beginning of the current row.
15
This method is limited to the current scrolling region.
16
See also: DoLineFeed
17
TAdTerminalBuffer Component 209
1
1
1
DoHorzTab
method
procedure DoHorzTab;
2
3
! Moves the cursor right to the next tab stop.
If there is no next tab stop, the cursor is moved to the last column of the line. If it already at
this position, it is not moved.
This method is limited to the current scrolling region.
4
5
See also: DoBackHorzTab, SetHorzTabStop
DoLineFeed
method
procedure DoLineFeed;
6
7
8
9
10
! Moves the cursor down one row.
This method has two modes of operation, distinguished by the value of UseNewLineMode.
If UseNewLineMode is False, the cursor is moved down a row, staying in the same column. If
the cursor is in the bottom row when this happens, the screen or scrolling region is scrolled
up. The new row so formed is initialized to space characters using the current colors and
attributes.
If UseNewLineMode is True, the cursor is moved down a row in the manner already
described, except this time it is moved to the first column.
This method is limited to the current scrolling region.
See also: DoCarriageReturn
11
12
DoVertTab
method
procedure DoVertTab;
! Moves the cursor down to the next tab stop.
13
If there is no next tab stop, the cursor is moved to the last row. If it already at this position, it
is not moved.
14
This method is limited to the current scrolling region.
15
See also: DoBackVertTab, SetVertTabStop
16
17
210 Chapter 8: The Terminal Components
1
1
EraseChars
method
1
procedure EraseChars(aCount : Integer);
! Erases characters from the current cursor position.
2
The erasing operation is done by replacing aCount characters with space characters, using
the current colors and attributes. The EraseChars method will automatically wrap and
continue at the end of rows, but it will not cause a scroll if the count requested exceeds the
bottom row. The cursor position is included.
3
4
This method is limited to the current scrolling region.
EraseFromBOL
method
procedure EraseFromBOL;
6
! Erases characters from the beginning of the row to the current cursor position.
The erasing operation is done by replacing the characters with space characters, using the
current colors and attributes. The cursor position is included.
7
This method is limited to the current scrolling region.
8
EraseFromBOW
method
9
procedure EraseFromBOW;
! Erases characters from the beginning of the screen to the current cursor position.
10
The erasing operation is done by replacing the characters with space characters, using the
current colors and attributes. The cursor position is included.
11
This method is not limited to the current scrolling region; it applies to the whole screen.
EraseLine
method
12
13
procedure EraseLine;
! Erases the current row.
The erasing operation is done by replacing the characters with space characters, using the
current colors and attributes.
14
This method is limited to the current scrolling region.
15
16
17
TAdTerminalBuffer Component 211
1
1
1
EraseScreen
method
procedure EraseScreen;
2
3
! Erases the entire screen.
The erasing operation is done by replacing the characters with space characters, using the
current colors and attributes.
4
This method is not limited to the current scrolling region; it applies to the whole screen. The
screen is erased by scrolling the entire scrollback view by RowCount rows. This has the effect
of erasing the screen, but also saves the previous version of the screen in the scrollback area.
5
EraseToEOL
6
method
procedure EraseToEOL;
! Erases characters from the current cursor position to the end of the row.
7
The erasing operation is done by replacing the characters with space characters, using the
current colors and attributes. The cursor position is included.
8
This method is limited to the current scrolling region.
EraseToEOW
method
9
procedure EraseToEOW;
10
11
! Erases characters from the current cursor position to the end of the screen.
The erasing operation is done by replacing the characters with space characters, using the
current colors and attributes. The cursor position is included.
This method is not limited to the current scrolling region; it applies to the whole screen.
12
13
ForeColor
property
property ForeColor : TColor
Default: clSilver
14
15
16
! Defines the foreground color for new text.
The ForeColor property determines the color in which new text will be displayed in the
terminal. Any new text sent to the Terminal will appear in this color.
See also: DefForeColor
17
212 Chapter 8: The Terminal Components
1
1
GetCharAttrs
method
1
procedure GetCharAttrs(var aValue : TAdTerminalCharAttrs);
TAdTerminalCharAttr = (tcaBold, tcaUnderline,
tcaStrikethrough, tcaBlink, tcaReverse, tcaInvisible);
2
TAdTerminalCharAttrs = set of TAdTerminalCharAttr;
3
! Returns the current set of display attributes.
The display attributes define the style of new text. The attributes are bold, underlined,
strikethrough, blink, reverse image (i.e., the text displayed in BackColor, the background in
ForeColor), and invisible.
4
If you wish to add a new attribute to the current set, write code like this:
6
var
Attrs : TAdTerminalCharAttrs;
begin
...
MyBuffer.GetCharAttrs(Attrs);
Attrs := Attrs + [tcaUnderline];
MyBuffer.SetCharAttrs(Attrs);
7
8
See also: GetDefCharAttrs, SetCharAttrs
9
GetDefCharAttrs
method
procedure GetDefCharAttrs(var aValue : TAdTerminalCharAttrs);
10
TAdTerminalCharAttr = (tcaBold, tcaUnderline,
tcaStrikethrough, tcaBlink, tcaReverse, tcaInvisible);
11
TAdTerminalCharAttrs = set of TAdTerminalCharAttr;
12
! Returns the default set of display attributes.
The display attributes define the style of new text. The attributes are bold, underlined,
strikethrough, blink, reverse image (i.e., the text displayed in BackColor, the background in
ForeColor), and invisible. If Reset is called, the current display attributes are set to the value
returned by GetDefCharAttrs.
13
14
See also: GetCharAttrs, Reset, SetDefCharAttrs
15
16
17
TAdTerminalBuffer Component 213
1
1
1
GetInvalidRect
method
function GetInvalidRect(var aRect : TRect) : Boolean;
2
3
4
5
6
7
8
9
! Returns the next invalid rectangle.
An invalid rectangle is a TRect structure that defines an area of the terminal screen that
needs repainting. The fields of the record, Left, Top, Right, and Bottom, define the area, not
in pixels, but in row and column numbers. Thus the rectangle (1, 2, 3, 4) describes the area
from the top left hand corner at row 2, column 1, to the bottom right hand corner at row 4,
column 3. The row and column numbers are absolute values and do not depend on the
current scrolling area, even if it activated. They are one-based values.
The return value is False if there was no invalid rectangle; otherwise, it is True and aRect has
the first invalid rectangle. The intent of this method is for the terminal display component to
continually call this method, repainting the areas of the screen affected, until the method
returns False.
Every change to the terminal is recorded by the terminal buffer as a list of invalid rectangles.
Conceivably, there could be very many by the time the terminal emulator component comes
to repainting. Rather than have the display component continually call this method to get
the full list of invalid rectangles, if there are a large number, the buffer uses an optimization
whereby it will merge all of the invalid rectangles into the one smallest rectangle that
overlaps all of the others. This is done automatically.
See also: HasCursorMoved, HasDisplayChanged
10
11
GetLineAttrPtr
method
function GetLineAttrPtr(aRow : Integer) : pointer;
! Returns a pointer to the first element of the display attributes array.
12
Each element in the display attributes array is a TAdTerminalCharAttrs set.
13
This method is not limited to the current scrolling region; it applies to the whole screen.
Thus, the pointer that is returned points to the data for column 1 of the required row.
14
15
16
17
214 Chapter 8: The Terminal Components
1
1
GetLineBackColorPtr
method
1
function GetLineBackColorPtr(aRow : Integer): pointer;
! Returns a pointer to the first element of the background colors array.
2
Each element in the background colors array is of type TColor.
This method is not limited to the current scrolling region; it applies to the whole screen.
Thus, the pointer that is returned points to the data for column 1 of the required row.
3
4
See also: BackColor, DefBackColor, GetLineForeColorPtr
GetLineCharPtr
method
function GetLineCharPtr(aRow : Integer): pointer;
6
! Returns a pointer to the first element of the characters array.
Each element in the characters array is either a single byte character, or a two-byte
UNICODE character. The original call to the Create constructor defines which it is.
7
This method is not limited to the current scrolling region; it applies to the whole screen.
Thus, the pointer that is returned points to the data for column 1 of the required row.
8
GetLineCharSetPtr
method
9
function GetLineCharSetPtr(aRow : Integer): pointer;
10
! Returns a pointer to the first element of the character set array.
Each element in the character set array is a byte. It is the responsibility of the owning
terminal display component to interpret the values.
11
This method is not limited to the current scrolling region; it applies to the whole screen.
Thus, the pointer that is returned points to the data for column 1 of the required row.
12
GetLineForeColorPtr
method
13
function GetLineForeColorPtr(aRow : Integer): pointer;
! Returns a pointer to the first element of the foreground colors array.
Each element in the foreground colors array is of type TColor.
This method is not limited to the current scrolling region; it applies to the whole screen.
Thus, the pointer that is returned points to the data for column 1 of the required row.
See also: DefForeColor, ForeColor, GetLineBackColorPtr
14
15
16
17
TAdTerminalBuffer Component 215
1
1
1
HasCursorMoved
method
function HasCursorMoved : Boolean;
2
3
4
! Returns whether the cursor has moved.
The terminal buffer maintains an internal flag that notes whether the cursor has moved at
any time. This method returns the value of this flag and then resets it to False. Hence, if the
return value is False, the cursor has not moved since the last time the method was called,
and if it returns True, the cursor has moved.
HasDisplayChanged
5
6
7
8
9
10
11
12
function HasDisplayChanged : Boolean;
! Returns whether the terminal has changed in appearance.
This method is a handy shortcut to be used instead of calling GetInvalidRect and getting
False.
See also: GetInvalidRect
InsertChars
14
15
16
! Inserts new chars at the cursor.
This method is equivalent to hit the space bar. The new chars inserted are initialized to space
characters, using the current colors and attributes. The chars that are advanced out of view
are deleted.
The number of characters to insert is aCount and is constrained by the current display
region.
! Inserts new lines at the cursor.
This method is equivalent to scrolling down the screen. The new rows inserted are
initialized to space characters, using the current colors and attributes. The rows that are
scrolled out of view are deleted.
The cursor is left in the same location.
This method is limited to the current scrolling region.
216 Chapter 8: The Terminal Components
1
method
procedure InsertLines(aCount : Integer);
17
1
method
procedure InsertChars(aCount : Integer);
InsertLines
13
method
MoveCursorDown
method
1
procedure MoveCursorDown(aScroll : Boolean);
! Moves the cursor down one row.
2
The cursor is moved onto the next row at the same column position. If the aScroll parameter
is False and the cursor is currently on the bottom row, it is not moved. If the aScroll
parameter is True and the cursor is on the bottom row, the screen or scrolling region is
scrolled up one line and the cursor remains in the same position. The new line inserted at
the bottom is initialized to space characters, using the current colors and attributes.
3
4
This method is limited to the current scrolling region.
MoveCursorLeft
method
procedure MoveCursorLeft(aWrap : Boolean; aScroll : Boolean);
! Moves the cursor left one position.
The column number of the cursor is decremented, unless the cursor is at the first position of
the row.
If, in fact, the cursor is at the row’s home position, the values for aWrap and aScroll come
into play. If aWrap is False, the cursor does not move. If aWrap is True, the cursor moves up
one row, and is positioned at the last column of that previous row. If, furthermore, the
cursor was originally at the first column of the top row and aScroll was False, the cursor does
not move. If, on the other hand, aScroll was True, the screen or scrolling region is scrolled
down one row and the cursor then moved to the last column of the top row. The new row is
initialized to space characters, using the current colors and attributes.
This method is limited to the current scrolling region.
See also: Col, MoveCursorRight, SetCursorPos
MoveCursorRight
6
7
8
9
10
11
12
method
procedure MoveCursorRight(aWrap : Boolean; aScroll : Boolean);
! Moves the cursor right one position.
13
14
The column number of the cursor is incremented, unless the cursor is at the last position
of the row.
If, in fact, the cursor is at the row’s last position, the values for aWrap and aScroll come into
play. If aWrap is False, the cursor does not move. If aWrap is True, the cursor moves down
one row, and is positioned at the first column of that next row. If, furthermore, the cursor
was originally at the last column of the bottom row and aScroll was False, the cursor does
15
16
17
TAdTerminalBuffer Component 217
1
1
1
not move. If, on the other hand, aScroll was True, the screen or scrolling region is scrolled up
one row and the cursor then moved to the first column of the bottom row. The new row is
initialized to space characters, using the current colors and attributes.
2
This method is limited to the current scrolling region.
3
See also: MoveCursorLeft, Col, SetCursorPos
MoveCursorUp
4
5
6
7
method
procedure MoveCursorUp(aScroll : Boolean);
! Moves the cursor up one row.
The cursor is moved onto the previous row at the same column position. If the aScroll
parameter is False and the cursor is currently on the top row, it is not moved. If the aScroll
parameter is True and the cursor is on the top row, the screen or scrolling region is scrolled
down one line and the cursor remains in the same position. The new line inserted at the top
is initialized to space characters, using the current colors and attributes.
This method is limited to the current scrolling region.
8
9
OriginCol
read-only property
property OriginCol : Integer
! Defines the column origin of the current scrolling region.
10
11
If the scrolling region is not in effect, OriginCol is 1, being the left-most column of the
screen. If the scrolling region is activated, OriginCol is the column number of the left-most
column of the scrolling region.
OriginCol is read-only. To set the scrolling region, call SetScrollRegion.
12
13
See also: OriginRow, SetScrollRegion, UseScrollRegion
OriginRow
read-only property
property OriginRow : Integer
14
! Defines the row origin of the current scrolling region.
15
If the scrolling region is not in effect, OriginRow is 1, being the top row of the screen. If the
scrolling region is activated, OriginRow is the row number of the first row of the scrolling
region.
16
OriginRow is read-only. To set the scrolling region, call SetScrollRegion.
See also: OriginCol, SetScrollRegion, UseScrollRegion
17
218 Chapter 8: The Terminal Components
1
1
Reset
method
1
procedure Reset;
! Resets the current colors and attributes to their defaults.
2
The screen is not changed by this method.
Row
property
3
4
property Row : Integer
! Defines the row of the cursor.
The Row property refers to the cursor. Reading the Row property returns the row number of
the cursor, and setting it moves the cursor to that row in the same column.
The value used for the Row property is one-based; in other words, rows are counted from 1.
If there are 24 rows down the terminal screen, the rows will be known as rows 1 to 24. If the
row number used is out of the range of the screen or the current scrolling region, it is forced
silently into bounds.
6
7
If a scrolling region is activated, the values for Row will be relative to the home position of
the scrolling region, not the home position of the screen itself.
8
See also: Col, RowCount, SetCursorPosition
9
RowCount
property
10
property RowCount : Integer
Default: 24
!
11
Defines the number of rows in the display view.
The RowCount property gets or sets the number of rows displayed by the terminal. Attempts
to set RowCount to a value greater than SVRowCount are ignored.
12
See also: ColCount, Row
13
14
15
16
17
TAdTerminalBuffer Component 219
1
1
1
SetCharAttrs
method
procedure SetCharAttrs(const aValue : TAdTerminalCharAttrs);
2
TAdTerminalCharAttr = (tcaBold, tcaUnderline,
tcaStrikethrough, tcaBlink, tcaReverse, tcaInvisible);
3
TAdTerminalCharAttrs = set of TAdTerminalCharAttr;
! Sets the current set of display attributes.
4
5
The display attributes define the style of new text. The attributes are bold, underlined,
strikethrough, blink, reverse image (i.e., the text displayed in BackColor, the background in
ForeColor), and invisible.
If you wish to add a new attribute to the current set, write code like this:
6
7
8
9
var
Attrs : TAdTerminalCharAttrs;
begin
...
MyBuffer.GetCharAttrs(Attrs);
Attrs := Attrs + [tcaUnderline];
MyBuffer.SetCharAttrs(Attrs);
See also: GetCharAttrs, SetDefCharAttrs
SetCursorPosition
10
11
12
procedure SetCursorPosition(aRow, aCol : Integer);
! Moves the cursor to a given position.
This method is equivalent to setting the Row and Col properties individually.
See also: Col, Row
13
14
15
16
17
220 Chapter 8: The Terminal Components
1
1
method
SetDefCharAttrs
method
1
procedure SetDefCharAttrs(const aValue : TAdTerminalCharAttrs);
TAdTerminalCharAttr = (tcaBold, tcaUnderline,
tcaStrikethrough, tcaBlink, tcaReverse, tcaInvisible);
2
TAdTerminalCharAttrs = set of TAdTerminalCharAttr;
3
! Sets the default set of display attributes.
The display attributes define the style of new text. The attributes are bold, underlined,
strikethrough, blink, reverse image (i.e., the text displayed in BackColor, the background in
ForeColor), and invisible. If Reset is called, the current display attributes are set to the value
set by SetDefCharAttrs.
4
See also: GetDefCharAttrs, Reset, SetCharAttrs
6
SetHorzTabStop
method
7
procedure SetHorzTabStop;
! Sets a horizontal tab stop at the current cursor column.
8
If a horizontal tab stop is already set for that column, this method has no effect.
9
See also: ClearAllHorzTabStops, ClearHorzTabStop
SetScrollRegion
method
10
procedure SetScrollRegion(aTopRow, aBottomRow : Integer);
! Sets the scrolling region.
11
A scrolling region is a range of lines (from aTopRow to aBottomRow) within which writes to
the screen and scrolling are restricted. This is often used for adding status lines that remain
visible on screen while other text in the Terminal is scrolled.
12
Calling SetScrollRegion will automatically activate the new scrolling region.
13
See also: UseScrollingRegion
SetVertTabStop
method
14
15
procedure SetVertTabStop;
! Sets a vertical tab stop at the current cursor row.
If a vertical tab stop is already set for that row, this method has no effect.
See also: ClearVertTabStop, ClearAllVertTabStops
16
17
TAdTerminalBuffer Component 221
1
1
1
SVRowCount
property
property SVRowCount : Integer
2
Default: 200
! Defines the number of rows in the scrollback view.
3
4
The SVRowCount property gets or sets the number of rows available in the scrollback
buffer. SVRowCount is being set, and the new value is less than RowCount, RowCount is
also set to the new value of SVRowCount.
See also: RowCount
5
6
UseAutoWrap
property
property UseAutoWrap : Boolean
! Defines what happens when a character is written at the last column of a row
7
8
9
10
UseAutoWrap only has an effect if the cursor is at the last column of a row. If UseAutoWrap
is False, the character is written at the last column of the current row, and the cursor does
not move. If UseAutoWrap is True, the character is written at the last column of the current
row, and then the cursor is moved to the first column of the next row, scrolling if necessary.
(In fact, this last operation is coded as a call to MoveCursorRight with aWrap and aScroll
both True.)
See also: MoveCursorRight
UseNewLineMode
11
12
13
14
property UseNewLineMode : Boolean
! Defines what the DoLineFeed method does.
If UseNewLineMode is False, DoLineFeed moves the cursor down one row, scrolling if
necessary. The cursor stays in the same column.
If UseNewLineMode is True, DoLineFeed moves the cursor down one row, scrolling if
necessary. The cursor is moved to the first column of the new row.
See also: DoLineFeed
15
16
17
222 Chapter 8: The Terminal Components
1
1
property
UseScrollRegion
property
1
property UseScrollRegion : Boolean
! Defines whether the scrolling region is active.
2
If UseScrollRegion is False, the scrolling region is inactive and writes of text and scrolling
apply to the whole screen. If UseScrollRegion is True, the scrolling region is in force and all
screen changes are limited to the scrolling region.
Calling SetScrollRegion automatically forces this property to True, the new scrolling is
brought into effect immediately.
3
4
See also: SetScrollRegion
UseWideChars
read-only, run-time property
6
property UseWideChars : Boolean
! Defines whether UNICODE characters are being stored by the terminal buffer.
7
The value of UseWideChars is set by a parameter to the Create constructor. In Delphi 1, this
value is always False; it is only an option for 32-bit programs.
8
See also: Create
WriteChar
method
9
10
procedure WriteChar(aCh : AnsiChar);
! Writes a single character at the cursor.
The cursor is advanced after the character is written. Please see the UseAutoWrap property
for a discussion of what happens if the cursor was on the last column of a row. The character
is written using the current colors and attributes.
11
12
See also: UseAutoWrap, WriteString
WriteString
method
13
14
procedure WriteString(const aSt : string);
! Writes a string at the cursor.
This method is coded as a simple loop calling WriteChar for each character in the string.
See also: WriteChar
15
16
17
TAdTerminalBuffer Component 223
1
1
1
The Terminal Parsers
2
The purpose of a terminal parser is to identify terminal control sequences in the stream of
data coming into the terminal (these control sequences are also more commonly known as
escape sequences) and return the command specified to the caller. The terminal parser is the
class that embodies the knowledge of the terminal’s escape sequences; if another terminal is
to be emulated then some of the first code to write is a new parser descendant to encapsulate
the knowledge about the new terminal to be supported.
3
4
5
6
7
8
9
The parser ancestor class
To facilitate this process, there is an ancestor parser class, the TAdTerminalParser class. The
main operations supported by this class are:
• Process a single character (virtual method).
• Clear the parser (virtual method).
• Get the command (property).
• Get the arguments (property).
• Get the sequence (property).
10
To gain a better understanding of these operations, we’ll look at them individually from a
high level.
11
Processing a single character is a virtual method that must be overridden in descendants. It
will return one of four states:
12
1. The parser did not understand the character in the current context, and so it should
be ignored.
2. The character is a displayable character and should be shown on the terminal screen.
13
14
15
16
3. The character started or continued an escape sequence, however that sequence is as
yet incomplete.
4. The character completed an escape sequence; the parser converted it into a command
and now this command must be processed.
It is up to the overridden method in a descendant class to determine how sequences are built
up, and converted into commands, and so on. For an example of this process, please refer to
the source code for the TAdVT100Parser class, the class that converts VT100 escape
sequences into commands.
17
224 Chapter 8: The Terminal Components
1
1
The operation of clearing the parser should reset the parser into a state such that no
sequence is being built up, and hence no knowledge of previous characters is maintained.
(The Clear operation is a virtual method of the class, which should be overridden in
descendants.)
If the character processing method returns a value that signifies that a command has been
identified, the Command property will return the current unprocessed command. This
property is reset to a null command if a sequence is being built up.
1
2
3
The Arguments property is an array property returning the arguments for the current
command. If there is no current command, the values are all zero. Essentially, certain
control sequences will define arguments or parameters for the command being defined, for
example, the command to move the cursor left might have an argument that defines how
many positions to move, whether just the implied 1, or several.
4
The Sequence property returns the actual escape sequence that has just been parsed. If the
current command is null, this property returns the empty string. This enables you to look at
the sequence, maybe to parse it outside the parser’s control, or to log it to a trace file.
6
The VT100 terminal parser
Async Professional provides one descendant of the ancestor parser class, TAdVT100Parser,
the parser for VT100 terminals. The VT100 parser class encapsulates the knowledge of the
standard VT100 escape sequences and to which command they refer. In addition, Async
Professional’s VT100 parser provides support for two extensions that, strictly speaking, are
not part of the standard VT100 command set. The first of these is the understanding of
escape sequences that set color: the original VT100 was strictly monochromatic. The second
extension is to support escape sequences that insert, delete and erase characters and lines.
The VT100 parser has two modes to reflect the behavior of the standard VT100 terminal.
The two modes are known as ANSI mode and VT52 mode. When in VT52 mode, the parser
only accepts standard VT52 escape sequences, together with the <Esc>< sequence (which is
used to switch back to ANSI mode). In ANSI mode, the VT52 sequences are ignored. The
command to switch from one to the other is processed immediately within the method that
processes characters as well being returned by it.
ANSI escape sequences for the VT100 terminal (and terminals that follow the ANSI
specification) are always of the following form:
<Esc>[P...PI...IF
<Esc> is the escape character (ASCII $27), ‘[’ is the left bracket, the Ps are ASCII characters
in the range of $30 to $3F, the Is are ASCII characters in the range of $20 to $2F, and the final
F is in the range of $40 to $7E. Because of this definition, if the parser does not recognize a
particular command, it can easily discard it—the end of the escape sequence is well defined.
7
8
9
10
11
12
13
14
15
16
17
The Terminal Parsers 225
1
1
1
2
3
4
5
6
7
8
9
With the VT100 terminal in ANSI mode, escape sequences either start with
<Esc>[,<Esc>#, <Esc>(, or <Esc>), or form a two character escape sequence <Esc>x
where x is the command identifier. The <Esc>[ sequences all follow the standard ANSI
format previously mentioned. The other three sequence types are three character sequences
with the final character identifying the command. Hence, the VT100 parser can know when
unknown escape sequences terminate.
With the VT100 parser in VT52 mode, all escape sequences are two character sequences of
the form <Esc>x with the x being the command identifier. The only exception is <Esc>Y
where the two characters following the Y also form part of the sequence (<Esc>Y is “cursor
position” and the two following characters encode the row and column numbers
respectively). Hence, the VT100 parser working in VT52 mode can know when unknown
sequences terminate.
The VT100 terminal also supports one-byte control characters, characters like tab, carriage
return, line feed and so on. The VT100 parser decodes these as well as escape sequences,
even when the control characters appear in the middle of escape sequences.
Table 8.1 defines the control characters and escape sequences understood by the VT100
parser. The individual characters in the escape sequence have been separated by spaces to
make the parameters stand out more. The values Pn, Ps, Pr, or Pc denote parameters,
meaning a numeric, switch, row or column value. Any one-byte control character not listed
here is ignored: it will not be acted on or displayed.
Table 8.1: VT100 parser control characters and escape sequences
10
11
12
13
14
15
16
Control
Sequence
TerminalMode
Description
ENQ ($05)
VT100/52
Generate answerback message.
BEL ($07)
VT100/52
Sound bell.
BS ($08)
VT100/52
Backspace (i.e., cursor left).
HT ($09)
VT100/52
Move to next horizontal tab stop.
LF ($0A)
VT100/52
Line feed or new line.
VT ($0B)
VT100/52
Processed as LF.
FF ($0C)
VT100/52
Processed as LF.
CR ($0D)
VT100/52
Move to position 1 on current line.
SO ($0E)
VT100
Select G1 character set.
SI ($0F)
VT100
Select G0 character set.
CAN ($18)
VT100/52
Cancel current escape sequence.
SUB ($1A)
VT100/52
Cancel current escape sequence.
Esc # 3
VT100
Line is double height, top half.
17
226 Chapter 8: The Terminal Components
1
1
Table 8.1: VT100 parser control characters and escape sequences (continued)
Control
Sequence
TerminalMode
Description
Esc # 4
VT100
Line is double height, bottom half.
Esc # 5
VT100
Line is single width, single height.
Esc # 6
VT100
Line is double width single height.
Esc # 8
VT100
Fill screen with E’s.
Esc ( A
VT100
Set G0 to UK charset.
Esc ( B
VT100
Set G0 to US charset.
Esc ( 0
VT100
Set G0 to special linedraw charset.
Esc ( 1
VT100
Set G0 to alternate ROM charset.
Esc ( 2
VT100
Set G0 to alternate ROM LD charset.
Esc ) A
VT100
Set G1 to UK charset.
Esc ) B
VT100
Set G1 to US charset.
Esc ) 0
VT100
Set G1 to special linedraw charset.
Esc ) 1
VT100
Set G1 to alternate ROM charset.
Esc ) 2
VT100
Set G1 to alternate ROM LD charset.
Esc 7
VT100
Save cursor and attributes.
Esc 8
VT100
Restore cursor and attributes.
Esc <
VT100
Enter ANSI mode (ignored).
Esc =
VT100
Enter application keypad mode.
Esc >
VT100
Enter numeric keypad mode.
Esc D
VT100
Index.
Esc E
VT100
Next line.
Esc H
VT100
Set tab stop at cursor.
Esc M
VT100
Reverse Index.
Esc [ Pn @
VT100 enh
Insert characters at cursor.
Esc [ Pn A
VT100
Cursor up.
Esc [ Pn B
VT100
Cursor down.
Esc [ Pn C
VT100
Cursor right.
Esc [ Pn D
VT100
Cursor left.
Esc [ Pr; Pc H
VT100
Cursor position.
Esc [ Ps J
VT100
Erase part of display to cursor.
Esc [ Ps K
VT100
Erase part of display from cursor.
Esc [ Pn L
VT100 enh
Insert lines.
Esc [ Pn M
VT100 enh
Delete lines.
1
2
3
4
6
7
8
9
10
11
12
13
14
15
16
17
The Terminal Parsers 227
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Table 8.1: VT100 parser control characters and escape sequences (continued)
Control
Sequence
TerminalMode
Description
Esc [ Pn P
VT100 enh
Delete characters at cursor.
Esc [ Pn X
VT100 enh
Erase characters at cursor.
Esc [ Ps c
VT100
What are you?
Esc [ Pr; Pc f
VT100
Cursor position.
Esc [ Ps g
VT100
Clear tab stops.
Esc [ Ps h
VT100
Set mode.
Esc [ Ps l
VT100
Reset mode.
Esc [ Ps; …;Ps m
VT100
Set attributes, including color.
Esc [ Ps n
VT100
Request terminal report.
Esc [ Ps; …;Ps q
VT100
Set LEDs.
Esc [ Pt; Pb r
VT100
Set scrolling region (top, bottom
row).
Esc [ 2; Ps y
VT100
Invoke confidence test.
Esc c
VT100
Reset.
Esc A
VT52
Cursor up.
Esc B
VT52
Cursor down.
Esc C
VT52
Cursor right.
Esc D
VT52
Cursor left.
Esc F
VT52
Set special character set.
Esc G
VT52
Set ASCII character set.
Esc H
VT52
Cursor to home.
Esc I
VT52
Reverse line feed.
Esc J
VT52
Erase to end of screen.
Esc K
VT52
Erase to end of line.
Esc Y
VT52
Direct cursor address.
Esc Z
VT52
Identify.
Esc <
VT52
Enter ANSI mode.
Esc =
VT52
Enter alternate keypad mode.
Esc >
VT52
Exit alternate keypad mode.
Note: Use the escape sequence <Esc>[2l to switch into VT52 mode.
17
228 Chapter 8: The Terminal Components
1
1
TAdTerminalParser Class
1
The TAdTerminalParser Class is the ancestor class that defines the functionality of a
terminal parser. A terminal parser is a class that knows how to identify and extract terminal
control sequences (also known as escape sequences) and convert them into commands and
arguments. The parser is not responsible for executing the commands; that task is left to the
terminal emulation component that uses the parser.
2
The methods defined by the TAdTerminalParser class are all virtual in order that they can be
overridden in descendant classes, but they are not defined as abstract. Instead, in this
ancestor class, they are all “do nothing” methods.
4
Hierarchy
3
6
TObject (VCL)
7
TAdTerminalParser (AdTrmPsr)
Properties
8
Argument
Command
ArgumentCount
Sequence
9
Methods
10
Clear
ProcessChar
Create
ProcessWideChar
11
12
13
14
15
16
17
TAdTerminalParser Class 229
1
1
1
Reference Section
Argument
read-only, array property
2
property Argument[aInx : Integer] : Integer
3
4
5
6
7
! Returns the arguments for the current command
When a terminal control sequence has been completely received, the ProcessChar method
will return pctComplete. Just prior to returning this success value, the parser will identify
the command described by the sequence, and extract out the arguments (or parameters)
from the sequence. The ArgumentCount property gives the number of arguments the parser
finds. You can retrieve the individual arguments by using the Argument property at this
time.
If you try and retrieve any arguments before a terminal control sequence has been
completely received, no error or exception is generated. The return value will always be zero
in this case.
In this ancestor class, the Argument property always returns zero.
8
9
See also: ArgumentCount, ProcessChar
ArgumentCount
read-only property
property ArgumentCount : Integer
10
11
12
13
14
! Returns the number of arguments for the current command
When a terminal control sequence has been completely received, the ArgumentCount
property will return the number of arguments or parameters the parser found in the
sequence. You can use this value to know how many arguments you can read from the
Argument array property.
If a sequence is still being built up, the ArgumentCount property will be zero. It will only
have a non-zero value after the ProcessChar method returns pctComplete (and before
ProcessChar is called again) and if the sequence had at least one argument.
In this ancestor class, the Argument property always returns zero.
See also: Argument, ProcessChar
15
16
17
230 Chapter 8: The Terminal Components
1
1
Clear
method
1
procedure Clear;
! Clears the internal state of the parser
2
By calling Clear, you will reset the parser to a state such that no sequence is being built up
and no command is pending.
Command
read-only property
3
4
property Command : Byte
! Returns the current command
Command returns the command identified by the ProcessChar method. Once ProcessChar
identifies a complete terminal control sequence, the parser will convert the sequence into the
relevant command and extract its arguments. It is at this point that the Command property
will be set to the command defined by the sequence. At any other time Command is set to
zero (the eNone constant from OOMISC.PAS). In other words, if ProcessChar returns
pctComplete, Command will be set to the relevant command defined by the just-received
sequence, and it will be reset to eNone the next time ProcessChar is called.
Command can take on any of the eXxx values defined in OOMISC.PAS (such as eCUB,
eVTS and so on). Please refer to the OOMISC.PAS source file for definitions of the
supported commands.
In this ancestor class, the Command property always returns eNone.
6
7
8
9
10
See also: ProcessChar
Create
method
constructor Create(aUseWideChar : Boolean);
11
12
! Creates the parser instance
Create allocates and initializes an instance of the parser class. The aUseWideChar parameter
defines whether the parser will only accept wide characters (True) or single byte characters
(False). If True, the ProcessWideChar method should be used to process the input data
stream, character by character; if False, the ProcessChar method should be used.
13
See also: ProcessChar, ProcessWideChar
15
14
16
17
TAdTerminalParser Class 231
1
1
1
ProcessChar
virtual method
function ProcessChar(aCh : AnsiChar) : TAdParserCmdType; virtual;
2
TAdParserCmdType = (pctNone, pctChar, pctPending, pctComplete);
! Processes a single character.
3
4
5
6
7
8
9
10
11
ProcessChar is the main workhorse of the parser class. It is the method that takes a character
and decides whether that character forms part of a terminal control sequence or is just one
that must be displayed. ProcessChar must maintain the current state of the parser (in other
words, whether the parser is building up a terminal control sequence, has just completed a
sequence, etc.).
ProcessChar can return one of several values. pctNone means that the character passed in
was not understood by the parser. Either the character was a single-byte control character
(in other words, a non-displayable character less than the space character) that would be
ignored by the original terminal, or the character terminated an escape sequence, but that
escape sequence was not known by the parser. The caller of ProcessChar can safely ignore
the problem since there is no recovery action to take because the parser has been left in a
correct state. pctChar means that the input character should be displayed on the terminal.
pctPending means that the input character formed part of an escape sequence that is being
built up, character by character. There is nothing further to do at this point since the escape
sequence has not yet been completed.
The last possible result value is pctComplete. This value indicates that the parser has
captured a complete terminal control sequence. Furthermore it indicates that the parser has
identified the command defined by the escape sequence and has set the Command property.
It will have extracted all of the command arguments or parameters and set both the
Argument and ArgumentCount properties. Finally, it will make sure that the Sequence
property returns the entire escape sequence.
12
In this ancestor class, the ProcessChar method always returns pctNone.
13
If ProcessChar is called for a parser that was created to expect wide characters, a parser
exception will be raised.
See also: Argument, ArgumentCount, Command, Create, ProcessWideChar, Sequence
14
15
16
17
232 Chapter 8: The Terminal Components
1
1
ProcessWideChar
method
1
function ProcessChar(aCh : WideChar) : TAdParserCmdType; virtual;
TAdParserCmdType = (pctNone, pctChar, pctPending, pctComplete);
2
! Processes a single character.
ProcessWideChar is the wide character (or UNICODE) version of ProcessChar. It works in
the same fashion with the exception of accepting UNICODE characters only. Please see the
ProcessChar method reference for details.
3
4
In this ancestor class, the ProcessWideChar method always returns pctNone.
If ProcessWideChar is called for a parser that was created to expect single-byte ASCII
characters, a parser exception will be raised.
6
See also: Create, ProcessChar
Sequence
read-only property
7
property Sequence : string
8
! Returns the current terminal control sequence.
Once ProcessChar identifies a complete terminal control sequence, and is about to return
pctComplete, the parser will set this property to the full sequence string. The property will
maintain its value until the next time ProcessChar is called.
If a sequence is being built up or there is no current command, the value of the Sequence
property is the empty string. Reading the Sequence property at the wrong time generates no
error or exception.
9
10
11
In this ancestor class, the Sequence property always returns the empty string.
12
See also: ProcessChar
13
14
15
16
17
TAdTerminalParser Class 233
1
1
1
TAdVT100Parser Class
2
The TAdVT100Parser class is the descendant of the TAdTerminalParser. It defines a parser
that understands VT100 terminal data streams.
3
Hierarchy
4
TObject (VCL)
! TAdTerminalParser (AdTrmPsr) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229
5
TAdVT100Parser (AdTrmPsr)
6
Properties
7
! ArgumentCount
8
Methods
Argument
9
! Command
InVT52Mode
! Clear
ProcessChar
! Create
! ProcessWideChar
10
11
12
13
14
15
16
17
234 Chapter 8: The Terminal Components
1
1
! Sequence
Reference Section
Argument
1
read-only, array property
2
property Argument[aInx : Integer] : Integer
! Returns the arguments for the current command.
3
Please see TAdTerminalParser for a description of how the Argument property works.
Some VT100 escape sequences use default values for certain arguments. For example, the
Cursor Right sequence is <Esc>[C, but it is also possible to specify the number of character
positions to move. In our case the sequence would be <Esc>[2C for two positions. The
VT100 parser in TAdVT100Parser would create a single argument of 1 (the default) for the
first case, and a single argument of 2 (an explicit argument) for the second case. There is no
way to find out if the parser has created an implicit default argument or used an explicit
argument, unless you wish to parse the Sequence property yourself. In general, this would
not matter: the value of an argument is all you need to process the command properly.
In certain cases the parser will be unable to determine the value of the default argument
since it would depend on information it does not have. An example for this is the Set
Scrolling Region command. This command has two arguments: the top row and the bottom
row of the scrolling region. The defaults are the top row and the bottom row of the screen
itself, neither of which are known by the parser. If the parser is unable to work out what the
default value of an argument is, it will set the relevant element of the Argument array
property to –1, meaning “default.”
There is a set of VT100 escape sequences that use the ‘?’ character in the parameter part of
the sequence. An example is <Esc>[?3h to set the VT100 terminal into 132-column mode.
The parser in the TAdVT100Parser class parses this escape sequence as having two
arguments, the ‘?’ and the ‘3’. Since the Argument property only returns integers, the ‘?’
character must be replaced by a special integer value, in this case, –2. Hence, in our example,
the command would have two arguments: –2 and 3. (The reason for this slightly eccentric
behavior, versus just ignoring the ‘?’ character altogether, is that ‘?’ signifies a DEC extension
rather than just a straightforward ANSI escape sequence. The parser needs to return this
information as well.)
4
6
7
8
9
10
11
12
13
14
15
16
17
TAdVT100Parser Class 235
1
1
1
InVT52Mode
read-only property
property InVT52Mode : Boolean
2
3
4
5
6
7
8
9
10
11
! Returns whether the terminal has been switched to VT52 mode.
The VT100 terminal can be switched between two modes: ANSI and VT52. The terminal
will understand different escape sequences in each of the two modes. Once the terminal is in
a given mode, the escape sequences for the other mode will not be understood or acted on.
The VT100 parser class tracks the escape sequences that switch the terminal from mode to
mode and will set an internal flag to denote whether the parser should identify ANSI escape
sequences or VT52 escape sequences. The mode switch sequences, <Esc>[?2l to switch to
VT52 mode and <Esc>< to switch back to ANSI mode, are the only sequences that are
tracked inside the parser, since they directly affect the operation of the parser.
The ProcessChar method will return the eDECANM result value if the parser identifies one
of the escape sequences that switch terminal modes. The InVT52Mode property will have
been set by the time that ProcessChar terminates and returns this value. Hence, if
ProcessChar returns eDECANM, you can check the value of the InVT52Mode to find out
which mode the terminal should now be in.
See also: ProcessChar
ProcessChar
method
function ProcessChar(aCh : AnsiChar) : TAdParserCmdType; override;
TAdParserCmdType = (pctNone, pctChar, pctPending, pctComplete);
! Processes a single character.
Please see TAdTerminalParser for a description of how the ProcessChar method works.
12
13
14
15
16
The VT100 terminal (and the ANSI specification) has one peculiarity that is generally not
well implemented by terminal emulators. The VT100 terminal supports several one-byte
control characters: line feed ($10), carriage return ($13), horizontal tab ($09), and so on.
Like the VT100 terminal, the VT100 parser class recognizes these one-byte control
characters, and acts upon them, even in the middle of an escape sequence. For example,
<Esc><CR>7 (where <CR> is the carriage return character) will be interpreted as
<CR><Esc>7; the <CR> being acted upon immediately, rather than being ignored. For this
example, the result values returned by ProcessChar for the three characters would be:
pctPending for the <Esc>; pctComplete for the <CR> (and the Argument,
ArgumentCount, Command and Sequence properties would be set accordingly); and,
finally, pctComplete for the <Esc>7 sequence (again setting the Argument,
17
236 Chapter 8: The Terminal Components
1
1
ArgumentCount, Command and Sequence properties accordingly). Notice that this
behavior necessitates that the parser save the not-quite-complete escape sequence
somewhere: this is done automatically.
1
The ANSI specification details the behavior if an <Esc> character appears in the middle of
an escape sequence: the second <Esc> cancels the original escape sequence and starts a new
one. So, for example, with <Esc[12<Esc>7, the second <Esc> character would cancel the
partial escape sequence that begins <Esc>[12 and start a new one.
2
Two one-byte control characters that have special significance are CAN ($18) and SUB
($1A). These cancel the current escape sequence (if one is being built up). If ProcessChar is
called with either of these characters, at any time, it will clear the VT100 parser and return
pctNone.
4
Although the ProcessChar method will successfully parse and decode the standard VT100
escape sequences, in general it will not carry out any of the decoded commands internally.
That job is left to the terminal component itself. However, there is an exception to the rule.
There are two escape sequences that affect the operation of the VT100 parser. The first is the
<Esc>[?2l sequence, which switches the terminal into VT52 mode. The second is the
<Esc>< sequence, which switches it back to ANSI mode. The importance of these two
modes is that the terminal recognizes different escape sequences in each of these modes and,
hence, the parser has to be able to know when to parse the ANSI sequences and when to
parse the VT52 sequences. For example, in ANSI mode the <Esc>D sequence is the same as
move the cursor one line down (the “Index” operation) whereas in VT52 mode it means
move the cursor one position left. If the parser did not recognize which state the terminal
was in, it may parse this sequence badly. Hence, the VT100 parser class maintains the
InVT52Mode property and this is set internally by the ProcessChar method to reflect the
current ANSI/VT52 mode on receipt of one of these two sequences.
See also: InVT52Mode
3
6
7
8
9
10
11
12
13
14
15
16
17
TAdVT100Parser Class 237
1
1
1
The TAdKeyboardMapping Class
2
The TAdKeyboardMapping class provides a simple, convenient method to specify the PC
keystrokes that map onto the emulated terminal keystrokes, and also what control sequence
those terminal keystrokes are going to send to the host computer.
3
4
5
6
7
8
9
10
11
12
13
14
15
16
There are three parts to the keyboard mapping. The first part is merely for convenience: it is
a mapping of the keyboard’s virtual key codes to the virtual key names. This mapping is
standard and is provided with Microsoft’s documentation. An example of a single mapping
is to specify that the virtual key code $70 is the F1 key, which is usually known by the name
“VK_F1” in Microsoft’s documentation.
The second part of the keyboard mapping is the definition of the character or control
sequence that is sent by the original keyboard to the host computer when a key is pressed.
For a VT100 terminal, for example, pressing the PF1 key will either send an <Esc>P or an
<Esc>OP sequence to the host, depending on the mode the terminal is in at the time. Again,
this mapping is standard and is provided as part of the terminal manufacturer’s
documentation.
Whereas the two mappings just described are fixed by standards, the last mapping is where
the creativity and individuality comes in. This set of mappings details which PC keystroke
gets mapped to which terminal keystroke. There are no standards for this (though some
mappings should be fairly obvious: for example, the up arrow key should be mapped to the
terminal’s up arrow key), and so you can customize to a large extent which keys perform
which action using this third mapping.
The TAdKeyboardMapping class is designed to be used by a terminal emulator. The
terminal component will trap the user’s keystroke and pass it onto the emulator. The
emulator will lookup the virtual key code in the keyboard mapping table and will retrieve
the virtual key name. It will then prefix this name with the names of the shift keys that are
active, and lookup this combination in the keyboard mapping class. If the lookup succeeds,
the keyboard mapping class returns the name of a terminal key and this, in turn, can be
looked up to find the character or control sequence that should be sent to the host computer.
If at any time a lookup fails, there is no special mapping for the keystroke and so the default
action takes over (for example, for an alphabetic key, the relevant character is sent to the
host computer).
Although it may seem excessive to have three lookups per keystroke to get to the final
sequence to send to the host, in reality the design it provides a balance between fast
conversion of keystrokes and ease of specification of the various mappings. The lookups are
performed using a hash table, which is considered the data structure of choice for this kind
of operation.
17
238 Chapter 8: The Terminal Components
1
1
The TAdKeyboardMapping class is convenient to use when you have to specify a large
number of mappings. There are two methods for loading a set of mappings: first from a
specially, yet simply, formatted text file, and, second, from a resource within the application.
Thus, it is possible to have a default mapping linked into the application, but also to be able
to provide a way of altering the mappings at run time. To help create the resource, the class
also has a method to write its current mapping set to a binary file, which can then be
compiled into a resource file. The following code shows this process:
var
KeyMap : TAdKeyboardMapping;
begin
KeyMap := TAdKeyboardMapping.Create;
try
KeyMap.LoadFromFile('ADKEYVT1.TXT');
KeyMap.StoreToBinFile('VT100.BIN');
finally
KeyMap.Free;
end;
end;
1
2
3
4
6
7
The code creates a TAdKeyboardMapping instance called KeyMap. A set of mappings is
then read from a file called ADKEYVT1.TXT (this file is a default set of keyboard mappings
for a VT100 emulator that is provided with Async Professional). The mappings are then
written out to a file called VT100.BIN. This latter file can be compiled into a resource using
the standard resource compiler that comes with Delphi (either BRCC.EXE or
BRCC32.EXE). The resource script (RC file) required for this is as follows:
8
9
10
MyVT100KeyMap RCDATA VT100.BIN
If you name the resource file VT100.RC, the resource compiler will create a file called
VT100.RES. Adding the resource to your application at that point is merely a case of adding
the following line to your project file and recompiling:
{$R VT100.RES}
Now you can call the LoadFromRes method of your TAdKeyboardMapping instance to load
this set of keyboard mappings.
11
12
13
14
15
16
17
The TAdKeyboardMapping Class 239
1
1
1
Hierarchy
TObject (VCL)
2
3
TAdKeyboardMapping (ADTrmKey)
Properties
Count
4
Methods
5
Add
Get
LoadFromRes
Clear
LoadFromFile
StoreToBinFile
6
7
8
9
10
11
12
13
14
15
16
17
240 Chapter 8: The Terminal Components
1
1
Reference Section
Add
1
method
2
function Add(const aKey : TAdKeyString;
const aValue : TAdKeyString) : Boolean;
3
TAdKeyString = string[63];
! Adds a new keyboard mapping to the instance.
4
The aKey string is the lookup value (the key string) and aValue is the string associated with
it. Add does not validate these two strings to be in any particular format, so it is possible to
fill the instance with nonsensical strings that would never be looked up.
If the key string and its value were successfully added, Add returns True. If the key string is
already present, Add will return False and leave the existing mapping as is.
Please see LoadFromFile for a discussion on how to define the key strings and their values.
6
7
See also: LoadFromFile
Clear
method
procedure Clear;
9
! Clears all keyboard mappings.
LoadFromFile and LoadFromRes automatically call Clear prior to loading a set of mappings.
If you wanted to load a set of mappings from another source, you would have to call Clear
first, and then Add for every mapping in your set.
See also: Add, LoadFromFile, LoadFromRes
Count
8
read-only, run-time property
property Count : Integer
10
11
12
13
! Determines the number of mappings in the class.
The Count property will return the number of different mappings contained in the class. It
does not differentiate between the different types of mappings.
14
15
16
17
The TAdKeyboardMapping Class 241
1
1
1
Get
method
function Get(const aKey : TAdKeyString) : TAdKeyString;
2
TAdKeyString = string[63];
! Returns the value of a looked up key string.
3
4
aKey is the key string to lookup. If it was found, the return value will be the string with
which it is associated. If it was not found, the return value is the empty string. No exception
is raised if the key string was not found.
See also: Add
5
6
LoadFromFile
method
procedure LoadFromFile(const aFileName : string);
! Loads a set of keyboard mappings from a text file.
7
The name of the file is given by aFileName.
8
The file is a text file in a particular format. LoadFromFile will follow these rules when
reading the file.
9
10
11
12
• Any completely blank line is ignored.
• Any line starting with a * is a comment and is skipped.
• Any line starting with at least one space is a detail line. A detail line consists of two
“words”, where a word is a set of up to 63 characters without an embedded space and
is case sensitive. Words are separated by spaces (not tab characters). Any other
characters appearing after the two words is taken to be a comment and is skipped.
• Any detail line that cannot be parsed is simply ignored.
• Any line that doesn’t match the above is skipped.
13
14
The words in a detail line have some formatting associated with them. The emulator
imposes this formatting so that it can generate the correct key strings when the user presses
a key. The LoadFromFile method does not validate these formats in any way.
“\e” in a word means the Escape character
15
\xnn, where nn is a hex number, represents that ASCII character
16
If you want to specify shift keys with virtual key names, use the mnemonics “shift”, “ctrl”,
and “alt.” Combine them with the virtual key name using the + sign. If you want to specify
more than one shifted key, make sure that they are in the order “shift”, “ctrl”, “alt.”
17
242 Chapter 8: The Terminal Components
1
1
An example of a virtual key code to virtual key name mapping would be:
1
* This is the definition of F1
\x70 VK_F1
2
An example of a defining the sequence to be sent by a terminal key is:
* This is the definition of PF1 on a VT100
* (it sends <Esc>OP)
DEC_PF1
\eOP
3
4
An example of mapping Alt+F1 so that it acts like the PF1 key on a VT100 is:
* Map Alt+F1 to PF1
alt+VK_F1 DEC_PF1
Let’s follow how the emulator would use these mappings. The user presses Alt+F1. The
emulator would convert the virtual key code returned by Windows (an integer equal to 70
hex) into a string (“\x70”). It would then lookup this string in the keyboard mapping object
and get the value “VK_F1” back. It then prefixes the “alt” keyword to this value to give
“alt+VK_F1” and looks this up. This returns the value “DEC_PF1”. This is in turn looked
up to return the value “\eOP”. The emulator then interprets this string as <Esc>OP in order
to send the correct sequence to the host. If a lookup fails at any stage, the keystroke is
assumed to be unmapped. If the emulator cannot interpret the final control sequence string,
it will just send it as is.
Please see ADKEYVT1.TXT for a complete set of mappings that define one way of mapping
the VT100 keys onto the PC keyboard.
The only errors than can occur with LoadFromFile are file I/O errors. Internally
LoadFromFile uses a string list (TStringList) to read the entire file into memory and so any
exceptions raised will be those raised by this class.
6
7
8
9
10
11
See also: Add
LoadFromRes
method
procedure LoadFromRes(
aInstance : THandle; const aResName : string);
! Loads a set of keyboard mappings from a resource.
12
13
14
The name of the resource is given by aResName, and the resource is to be found in the
module instance given by hInstance. The resource must be in the binary format created by
the StoreToBinFile method. If any error occurs during the load process, for example the
resource cannot be found or the resource is not in the correct format, the mappings are
cleared. No exception is raised.
15
16
17
The TAdKeyboardMapping Class 243
1
1
1
StoreToBinFile
method
procedure StoreToBinFile(const aFileName : string);
2
! Stores the current set of mappings to file.
3
The name of the file is given by aFileName. The binary file so created can be compiled into a
resource that can be read by LoadFromRes.
4
The only errors than can occur are file I/O errors. Internally StoreToBinFile uses a file
stream (TFileStream) and so any exceptions raised will be those raised by this class.
5
6
7
8
9
10
11
12
13
14
15
16
17
244 Chapter 8: The Terminal Components
1
1
The TAdCharSetMapping Class
1
The TAdCharSetMapping class provides a method to emulate the different character sets
used by terminals by using glyphs from different fonts.
2
One of the problems of emulating a terminal with a Windows program is that terminals,
especially the older ones, use specialized glyphs that are not available in the normal
Windows fonts. The character set mapping class attempts to work around this problem by
enabling the emulator to obtain the glyphs for different character sets from different fonts.
3
Before proceeding with the character set mapping class, we should define a few terms. A
character is a binary value, usually byte-sized. The way we usually think of a character is
both as its binary value and as its visual form. Hence, when we think of the character ‘a’, for
example, we think of its value ($61) and of its visual form (a drawing of a lowercase letter ‘a’,
like the one you just read just now). However, the link between the character ‘a’ and the
visual form of the character is not fixed. Different fonts may display the character ‘a’ in
different ways, and some visual representations (what are known as glyphs) may look
nothing like a lowercase ‘a’. An example is the Symbol font where the character ‘a’ is drawn as
a Greek lowercase α. With terminals, there may be several character sets that are available at
once, and these character sets function in the same way as fonts. On the VT100, for example,
there are two main character sets: the standard one and the special graphics one, which
displays the line draw characters. In the first, the character ‘a’ is drawn as a lowercase ‘a’. In
the second it is drawn as a checkerboard glyph.
The character set mapping class is designed to be used by a terminal emulator. When the
terminal display needs to be painted, the emulator will use a character set mapping class to
identify which glyphs need to be painted on the terminal window. For this it will pass a
string of characters to the mapping class, together with the character set to be used. The
TAdCharSetMapping object will return a script to the emulator. This script will consist of a
series of text drawing commands all of the form: “using font X, draw string Y.” Usually the
script will consist of just one command, since the characters will generally all come from the
standard ASCII set and hence can be drawn using just one font.
4
6
7
8
9
10
11
12
13
14
15
16
17
The TAdCharSetMapping Class 245
1
1
1
2
3
4
5
6
7
8
9
10
The TAdCharSetMapping class gets its mapping data from one of two sources: a specially
formatted text file, or a resource linked into the application. Thus it is possible to have a
default mapping linked to the application, but, also, to be able to provide a way of altering
the mappings at run time (maybe to suit the fonts on the user’s machine). To help create the
resource the character set mapping class has a method to write its current mapping set to a
binary file, which can then be compiled into a resource file. The following code shows this
process:
var
CharSetMap : TAdCharSetMapping;
begin
CharSetMap := TAdCharSetMapping.Create;
try
CharSetMap.LoadFromFile('ADCHSVT1.TXT');
CharSetMap.StoreToBinFile('VT100.BIN');
finally
CharSetMap.Free;
end;
end;
The code creates a TAdCharSetMapping instance called CharSetMap. A set of mappings is
then read from a file called ADCHSVT1.TXT (this file is a default set of character set
mappings for a VT100 emulator that is provided with Async Professional). The mappings
are then written out to a file called VT100.BIN. This latter file can be compiled into a
resource using the standard resource compiler that comes with Delphi (either BRCC.EXE or
BRCC32.EXE). The resource script (RC file) required for this is as follows:
MyVT100CharSetMap RCDATA VT100.BIN
11
12
13
If you name the resource file VT100.RC, the resource compiler will create a file called
VT100.RES. Adding the resource to your application at that point is merely a case of adding
the following line to your project file and recompiling:
{$R VT100.RES}
Now you can call the LoadFromRes method of your TAdCharSetMapping instance to load
this set of character set mappings.
14
15
16
17
246 Chapter 8: The Terminal Components
1
1
Hierarchy
1
TObject (VCL)
2
TAdCharSetMapping (ADTrmMap)
Properties
3
Count
4
Methods
Add
GetNextDrawCommand
Clear
LoadFromFile
GenerateDrawScript
LoadFromRes
StoreToBinFile
6
7
8
9
10
11
12
13
14
15
16
17
The TAdCharSetMapping Class 247
1
1
1
Reference Section
Add
method
2
3
function Add(const aCharSet : TAdKeyString;
aFromCh : AnsiChar; aToCh : AnsiChar;
aFont : TAdKeyString; aGlyph : AnsiChar) : Boolean;
TAdKeyString = string[63];
4
5
6
7
8
9
10
11
12
13
14
! Adds a new character set mapping to the instance.
The aCharSet parameter is the name of the character set. This name is defined by the
emulator if there is no standard name for it. The character set name must be unique. In other
words, different character sets will have different names. aFromCh and aToCh define an
inclusive range of characters for the mapping (if aToCh equals aFromCh, the mapping is for
a single character). aFont is the name of the font from which the glyph or glyphs are taken.
aGlyph is the glyph from which the mapping starts. aFromCh is mapped to aGlyph, the
character after aFromCh is mapped to the glyph after aGlyph, and so on until aToCh.
The result value is True if the character set mapping was added, False otherwise. The latter
result would mean that the combination of aCharSet and the supplied range clashed with a
mapping already present.
To help in designing portable character set mappings, there is one special value that can be
used for the font name. If the name is “<Default>”, then the emulator will use the currently
defined font for the terminal component to display text.
For example, suppose you wish to add a character set mapping for the standard ASCII
characters, using Courier New as the font. This is the statement you would use:
var
MyMap : TAdCharSetMapping;
begin
...
if not MyMap.Add(
'MyCharSet', ' ', '~', 'Courier New', ' ') then
..mapping not added..
This tries to add a mapping for all of the characters between space and ‘~’ in the MyCharSet
character set to that same characters (i.e., glyphs) from the Courier New font.
15
16
17
248 Chapter 8: The Terminal Components
1
1
Clear
method
1
procedure Clear;
! Clears all character set mappings.
2
LoadFromFile and LoadFromRes automatically call Clear prior to loading a set of mappings.
If you wanted to load a set of mappings from another source, you would have to call Clear
first, and then Add for every mapping in your set.
3
See also: Add, LoadFromFile, LoadFromRes
4
Count
read-only, run-time property
property Count : Integer
! Determines the number of mappings in the class.
6
The Count property will return the number of different mappings contained in the class. It
does not differentiate between the different character sets or ranges. It is of use primarily for
writing out the mappings to a file or other object.
7
GenerateDrawScript
8
method
procedure GenerateDrawScript(
const aCharSet : TAdKeyString; const aText : string);
! Generates a draw script from a string.
The emulator, when it needs to display text on the terminal window, will separate out the
text to be drawn into strings from different character sets. For each individual string it will
call the GenerateDrawScript method of its internal character set mapping object to generate
a series of drawing commands that can be used to draw the text on the screen. Each
command will be of the form “switch to font X, write string Y.” The emulator then reads the
commands one at a time using the GetNextDrawCommand method, and draws the text in
the required font.
If GenerateDrawScript is called before all of the commands from the previous script have
been read, the previous commands are destroyed and will no longer be available.
The aCharSet parameter is the name of the character set. aText is the string of characters that
have to be drawn using the given character set. The GenerateDrawScript method will work
out which fonts and glyphs are required to draw the characters and generate a list of drawing
commands that, when executed one after the other, will produce the required effect.
9
10
11
12
13
14
15
16
See also: GenNextDrawCommand
17
The TAdCharSetMapping Class 249
1
1
1
2
3
4
5
6
7
8
9
GetNextDrawCommand
method
function GetNextDrawCommand(
var aFont : TAdKeyString; var aText : string) : Boolean;
! Retrieves the next draw command from the current script.
The emulator, when it needs to display text on the terminal window, will separate out the
text to be drawn into strings from different character sets. For each individual string it will
call the GenerateDrawScript method of its internal character set mapping object to generate
a series of drawing commands that can be used to draw the text on the screen. Each
command will be of the form “switch to font X, write string Y.” The emulator then reads the
commands one at a time using the GetNextDrawCommand method, and draws the text in
the required font.
The GetNextDrawCommand method returns True if there is another command, and sets
aFont to the font name required and aText to the string that needs to be drawn in that font. It
returns False if there are no more commands in the current script. The emulator will
continue to call GetNextDrawCommand and draw the specified text in the given font until
the method returns False and the script is exhausted.
See also: GenerateDrawScript
LoadFromFile
method
procedure LoadFromFile(const aFileName : string);
10
! Loads a set of character set mappings from a text file.
The name of the file is given by aFileName.
11
The file is a text file in a particular format. LoadFromFile will follow these rules when
reading the file.
12
• Any completely blank line is ignored.
13
• Any line starting with a * is a comment and is skipped.
• Any line starting with at least one space is a detail line. A detail line consists of five
14
15
16
“words.” A word is defined as a set of up to 63 characters without an embedded space,
or as a set of up to 63 characters enclosed by quote marks (single or double). A word is
case sensitive. Words are separated by spaces (NOT tab characters). Any other
characters appearing after the five words is taken to be a comment and is skipped.
• Any detail line that cannot be parsed is simply ignored.
• Any line that doesn’t match the above is skipped.
17
250 Chapter 8: The Terminal Components
1
1
The five words, in order, are the same as the five parameters to the Add method. They
denote the following:
• The character set name.
1
2
• The from character for the range.
• The to character for the range.
3
• The font name.
4
• The from glyph for the range.
The words that define a character can either be the character itself or be the hex
representation of the character in the form \xnn with “nn” being the hex value. Hence, the
space character would be represented by \x20, and the character ‘a’ would be shown by the
single letter a.
6
To help in designing portable character set mappings, there is one special value that can be
used for the font name. If the name is “<Default>”, then the emulator will use the currently
defined font for the terminal component to display text.
7
An example of defining a character set mapping for the standard ASCII characters would be
8
* This defines the standard ASCII characters
MyCharSet \x20 ~ 'Courier New' \x20
9
This would be read as: the character set name is “MyCharSet”; the range of characters is
from the space character to the ‘~’ character, inclusive; the font name is “Courier New”; and
the starting glyph is that for the space character.
10
Please see ADCHSVT1.TXT for a complete set of mappings that define one way of mapping
the VT100 character sets keys onto standard Windows fonts.
11
The only errors than can occur with LoadFromFile are file I/O errors. Internally
LoadFromFile uses a string list (TStringList) to read the entire file into memory and so any
exceptions raised will be those raised by this class.
12
13
See also: Add
14
15
16
17
The TAdCharSetMapping Class 251
1
1
1
2
3
4
5
6
LoadFromRes
procedure LoadFromRes(
aInstance : THandle; const aResName
method
: string);
! Loads a set of keyboard mappings from a resource.
The name of the resource is given by aResName, and the resource is to be found in the
module instance given by hInstance. The resource must be in the binary format created by
the StoreToBinFile method. If any error occurs during the load process, for example the
resource cannot be found or the resource is not in the correct format, the mappings are
cleared. No exception is raised.
StoreToBinFile
method
procedure StoreToBinFile(const aFileName : string);
! Stores the current set of mappings to file.
7
The name of the file is given by aFileName. The binary file so created can be compiled into a
resource that can be read by LoadFromRes.
8
The only errors than can occur are file I/O errors. Internally StoreToBinFile uses a file
stream (TFileStream) and so any exceptions raised will be those raised by this class.
9
10
11
12
13
14
15
16
17
252 Chapter 8: The Terminal Components
1
1
The TAdTerminalEmulator Class
1
The TAdTerminalEmulator class is the base class for all terminal emulators. An emulator is
designed to work hand-in-hand with a terminal component to provide the look and feel of a
particular terminal. The emulator does all the hard work: it interprets the input data stream,
looking for terminal control sequences and ordinary text characters and processing them; it
accepts all keystrokes from the terminal component, identifies them and converts them to
output the correct terminal sequence. It also maintains the buffer that describes what the
terminal display looks like, the text characters, colors, attributes and character sets. It has a
character set mapping object that enables it to identify which fonts are used for which glyphs
for display. Finally, and possibly the most important, it uses the terminal component’s
canvas object to draw a representation of the current view of the terminal.
2
Async Professional provides two descendants of TAdTerminalEmulator. The first is the
simplest emulator of all, the teletype or TTY emulator, TAdTTYEmulator. This emulator
performs no keystroke conversion, no character set mapping, and no parsing of the input
stream. Every character that is received is drawn directly onto the terminal display. All
standard keystrokes (alphabetic characters, numeric characters, and control characters) are
sent directly to the host computer. All other keystrokes (function keys, cursor movement
keys, and the like) are ignored. When you drop a terminal component onto the form and do
not use an emulator component, the terminal component will create an internal instance of
the TTY emulator and use that instead.
6
The second emulator provided by Async Professional is the VT100 terminal emulator,
TAdVT100Emulator. This emulator provides a complete implementation of a standard
VT100 terminal. Features provided by this emulation include:
3
4
7
8
9
10
• Double height and double width characters.
11
• Support for the keyboard LEDs.
12
• The ability to switch character sets to use the line draw characters.
• Applying the scrolling region.
13
• Support for the various VT100 modes, including keyboard modes.
14
15
16
17
The TAdTerminalEmulator Class 253
1
1
1
2
The emulator also provides support for the following features that are not part of the
standard VT100 specification, but are nevertheless generally expected to be present in an
emulation. These features are extracted from the ANSI specification and the VT220
specification:
• Support for erasing, deleting and inserting characters.
3
4
5
• Support for the different ANSI color attributes.
Finally, please note that the emulator does not support the following, sometimes optional,
hardware characteristics of some VT100 terminals. In fact, in response to a “What Are You?”
request (<Esc>[c) the VT100 terminal emulator will respond as a “basic VT100 with no
options” (<Esc>[?;0c).
• Interlace mode (switching between 240 & 480 scan lines per frame).
6
• Smooth scrolling (the emulator performs jump scrolling all the time).
7
• STP processor option.
• AVO (advanced video option).
8
9
10
11
12
• GPO (graphics processor option).
To use an emulator component you would drop one on the form and then drop a terminal
component onto the same form. The terminal component would find the emulator
component and link up with it. At that point the terminal and emulator components will
function as one composite component and will perform all the necessary work to make the
combination act as an original terminal. There is no extra work to be done on your part.
Obviously, if you wish to alter the behavior of an emulator, you will need to know about its
properties, events and methods, and understand the use of the internal objects the emulator
utilizes. If you wish to write a different terminal emulator then you would also need to know
this extra information, but for general use of the Async Professional terminal component
family, it will generally be “drop and go.”
13
14
15
16
17
254 Chapter 8: The Terminal Components
1
1
Hierarchy
1
TComponent (VCL)
! TApdBaseComponent (OOMisc) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
TAdTerminalEmulator (ADTrmEmu)
3
Properties
Buffer
NeedsUpdate
CharSetMapping
Parser
KeyboardMapping
Terminal
2
! Version
Methods
4
6
BlinkPaint
KeyDown
LazyPaint
GetCursorPos
KeyPress
Paint
7
8
9
10
11
12
13
14
15
16
17
The TAdTerminalEmulator Class 255
1
1
1
Reference Section
BlinkPaint
virtual method
2
procedure BlinkPaint(aVisible : Boolean); virtual;
3
4
5
6
7
8
9
! Paints the blinking text.
Most terminals support blinking text. For a Windows emulation of blinking text, the text
must first be drawn normally, then a short time later that text is erased so that it is essentially
invisible, and then a further short time later the text is redrawn normally. The effect for the
user is that the text blinks on and off regularly.
The terminal component performs no painting of its own. Instead it defers that job to the
emulator component, where the knowledge of such processing for a given terminal is
embodied. Instead the terminal component maintains the blink timer, a timer object that
fires at regular intervals. The terminal component will call the emulator’s BlinkPaint method
to either display all of the blinking text (aVisible is True) or to erase it and just show the
background color (aVisible is False). It is up to the emulator to maintain a list of regions of
the terminal display that must be drawn and redrawn in this fashion.
See also: LazyPaint, Paint
Buffer
read-only, run-time property
property Buffer : TAdTerminalBuffer
10
! Returns the terminal buffer.
12
The Buffer property is the data structure that holds the elements that go to make up the
visual representation of the terminal. These elements are the text characters, the foreground
and background colors, the attributes of the text (underlined, blinking, and so on), and the
character set ids for the characters.
13
The emulator creates a buffer object in its Create constructor and frees it in the Destroy
destructor. It is not possible to replace the buffer object in between those two times.
11
14
CharSetMapping
read-only, run-time property
property CharSetMapping : TAdCharSetMapping
15
16
17
! Returns the character set mapping object for the emulator.
The CharSetMapping property is the data structure that holds the various character-set-tofont-glyph mappings for the emulator. The emulator will attempt to map a character in a
particular character set to a glyph in a font, before displaying that character in the terminal
component’s window.
256 Chapter 8: The Terminal Components
1
1
The emulator creates a character set mapping object in its Create constructor and frees it in
the Destroy destructor. It is not possible to replace the character set mapping object in
between those two times. However, it is possible to replace the set of mappings for the
character set mapping object: just call its LoadFromFile or LoadFromRes methods. This will
have the effect of changing the functionality of the emulator.
Note that some emulators will not have a character set mapping object; reading
CharSetMapping will return nil. This is because the emulation concerned—for example, the
TTY emulator—does not require any character set mapping services.
GetCursorPos
1
2
3
4
virtual method
procedure GetCursorPos(var aRow, aCol : Integer); virtual;
! Returns the cursor position.
The emulator maintains the position of the terminal’s cursor. The terminal component can
call the GetCursorPos method to get the row and column values for the cursor’s position, so
that it can draw a blinking caret at this position on its client window.
6
7
Using terminal terminology, the “cursor” means the keyboard edit point. It is the place
where new text data will be written to the screen. In Windows terminology however, this is
known as the caret, the cursor being the mouse pointer.
8
KeyboardMapping
9
read-only, run-time property
property KeyboardMapping : TAdKeyboadMapping
10
! Returns the keyboard mapping object for the emulator.
The KeyboardMapping property is the data structure that holds the various keystroke-toterminal control sequence mappings for the emulator. The emulator will attempt to map a
keystroke provided by a call to the KeyDown method into a terminal control sequence to be
sent to the host computer by calling the Get method of the keyboard mapping object.
The emulator creates a keyboard mapping object in its Create constructor and frees it in the
Destroy destructor. It is not possible to replace the keyboard mapping object in between
those two times. However, it is possible to replace the set of mappings for the keyboard
mapping object: just call its LoadFromFile or LoadFromRes methods. This will have the
effect of changing the functionality of the emulator.
Note that some emulators will not have a keyboard mapping object; reading
KeyboardMapping will return nil. This is because the emulation concerned—for example,
the TTY emulator—does not require any keyboard mapping services.
11
12
13
14
15
16
17
The TAdTerminalEmulator Class 257
1
1
1
KeyDown
virtual method
procedure KeyDown(var Key : Word; Shift: TShiftState); virtual;
2
3
4
5
6
7
8
9
10
! Processes a key down message from the terminal component.
The terminal component performs no keystroke processing of its own. Instead it defers that
job to the emulator component, where the knowledge of such processing for a given
terminal is embodied.
The terminal component’s KeyDown method—the standard VCL KeyDown method for
TWinControl descendants—merely calls the KeyDown method of its attached emulator and
performs no other processing of its own. As a convenience, the terminal will also call the
emulator’s KeyDown method for system keys (those reported by a WM_SYSKEYDOWN
message), as well as those keystrokes that are reported through the terminal’s keyboard
hook interface.
The emulator will use its keyboard mapping object to determine what to do with the
keystroke. If there is a conversion defined, the emulator will use the terminal’s ComPort
property to send the character sequence associated with the keystroke.
If there is no conversion defined in the mapping object the emulator ignores the keystroke. If
the keystroke was an ordinary alphabetic key, the emulator’s KeyPress method will be
eventually called by the terminal component with the character. At this point the emulator
can send the character to the host computer. Hence, the set of mappings for the keyboard
mapping object does not have to specify lower and upper case alphabetic, or numeric
mappings.
KeyPress
11
12
13
14
15
16
procedure KeyPress(var Key : AnsiChar); virtual;
! Processes a key press message from the terminal component.
The terminal component performs no keystroke processing of its own. Instead it defers that
job to the emulator component, where the knowledge of such processing for a given
terminal is embodied.
The terminal component’s KeyPress method—the standard VCL KeyPress virtual method
for TWinControl descendants—merely calls the KeyPress method of its attached emulator.
The Key parameter defines the key pressed.
The usual job of the emulator at this point is to send the character to the host computer by
means of the terminal component’s ComPort property.
17
258 Chapter 8: The Terminal Components
1
1
virtual method
LazyPaint
virtual method
1
procedure LazyPaint; virtual;
! Processes a lazy display paint request from the terminal component.
The terminal component performs no painting of its own. Instead it defers that job to the
emulator component, where the knowledge of such processing for a given terminal is
embodied.
2
3
There are two types of painting to be done. The first is painting because the new data that
has come from the serial device needs to be shown (this could be triggered by the lazy
display processing of the terminal, for example). The second is painting due to all or part of
the terminal component’s client window being invalidated and Windows has issued a
WM_PAINT message.
4
The LazyPaint method is called in the former case. The emulator has been parsing the
incoming data stream and altering the buffer to reflect the new text and other changes
embedded in the stream. The terminal component has determined that the new changes
need to be shown on the client window. There are three ways the terminal can make that
determination. The first is when the emulator sets its NeedsUpdate property to True to
denote that the terminal display has changed and the new data needs to be shown. The
second is when the terminal component is in lazy display mode (its UseLazyDisplay
property is True), and a certain number of bytes (given by the TAdTerminal.LazyByteDelay
property) has been processed. The third is again when the terminal component is in lazy
display mode and the required amount of time, given by the LazyTimeDelay property, has
elapsed.
6
7
8
9
10
The emulator needs to interrogate its terminal buffer object to find out the changed
character cells in the terminal display and to display them on the terminal component’s
canvas.
11
See also: BlinkPaint, Paint
12
NeedsUpdate
read-only, run-time property
13
property NeedsUpdate : Boolean
14
! Defines whether the terminal display has changed.
The emulator sets its NeedsUpdate property to True when it makes a change to its terminal
buffer object, for example, due to a new character being written to the terminal, or due to
some other change, like scrolling, that has happened. The terminal component defined by
the Terminal property will read this property every now and then, and if True will call the
emulator’s LazyPaint method so that the emulator can update the view of the terminal.
15
16
17
The TAdTerminalEmulator Class 259
1
1
1
Paint
virtual method
procedure Paint; virtual;
2
3
4
5
6
! Processes a paint request from the terminal component.
The terminal component performs no painting of its own. Instead it defers that job to the
emulator component, where the knowledge of such processing for a given terminal is
embodied.
There are two types of painting to be done. The first is painting because the new data that
has come from the serial device needs to be shown (this could be triggered by the lazy
display processing of the terminal, for example). The second is painting due to all or part of
the terminal component’s client window being invalidated and Windows has issued a
WM_PAINT message. The Paint method is called in the latter case. The emulator should
find out the clipping region of the invalidated window and redraw the text contained there,
using the internal buffer object to define the colors, attributes, character sets and so on.
7
The Paint method of the emulator is called by the Paint method of the terminal component,
the standard VCL virtual method for TWinControl descendants.
8
See also: BlinkPaint, LazyPaint
Parser
run-time property
9
property Parser : TAdTerminalParser
10
11
12
13
14
15
! Accesses the emulator’s terminal control sequence parser.
The Parser property is the engine that interprets the incoming data stream for the emulator.
It is the parser that decides which sets of characters are terminal control sequences and
which are merely characters to be displayed.
The emulator creates a parser object in its Create constructor and frees it in the Destroy
destructor. Indeed, some emulator descendant classes do not require a parser object (for
example, the TTY emulator) and hence do not ever create one. Hence, be aware that reading
the Parser property may return nil.
The emulator will call the ProcessChar method of the parser—if there is one—for every
character it receives from the terminal component. It will act on the return value of this call,
making use of the parser’s Command, Sequence, and Argument properties to alter the
buffer object to reflect the new view of the terminal.
16
17
260 Chapter 8: The Terminal Components
1
1
It is possible to replace the parser at run time. This will have the effect of altering the
behavior of the emulator component. The class will dispose of the old parser and start using
the new one. Be aware that it is possible to switch parsers while the old parser is in the
middle of processing a terminal control sequence. In that case, the partially received control
sequence is lost. It is best to replace the parser just after calling the old parser’s ProcessChar
method, if it does not return pctPending.
Terminal
1
2
3
property
4
property Terminal : TAdTerminal
! Defines the visual terminal component.
The Terminal property defines the terminal component whose canvas is used for drawing
the terminal display, and which provides access to the keyboard for the emulator. The
terminal component also hold the reference to the serial device through its ComPort
property.
The terminal component and emulator component use each other to provide the correct
functionality of the terminal emulation. The terminal component holds a link to the COM
port that provides the incoming data stream, and passes all of this data directly to the
emulator. The emulator, by use of its terminal parser instance, will interpret the data and
modify the buffer (a TAdTerminalBuffer instance) to hold the new representation of the
terminal display. The terminal component will also pass to the emulator all keystrokes
entered by the user, so that the emulator, with the use of its keyboard mapping component,
can identify the keystroke and convert it into the correct response to the host computer.
Every now and then, especially if the terminal component is using lazy writing, the terminal
will tell the emulator to draw a depiction of the current state of the terminal display on the
terminal component’s canvas.
If you set the Terminal property to nil, the emulator will detach itself from the previous
terminal component. This component will then use its internal TTY emulator.
6
7
8
9
10
11
12
See also: TAdTerminal.ComPort, TAdTerminal.Emulator
13
14
15
16
17
The TAdTerminalEmulator Class 261
1
1
1
The TAdTTYEmulator Class
2
The TAdTTYEmulator class emulates a teletype terminal, one that doesn’t support any
terminal control sequences and one that merely displays every character received. Similarly
there is no conversion of keystrokes either: if a key for a displayable characters is pressed,
that character is sent to the host without interpretation.
3
4
5
6
The following properties are nil for a TAdTTYEmulator instance: Parser,
KeyboardMapping, and CharSetMapping. These objects are not required by the TTY
emulator and so are never created. The TTY emulator does maintain a terminal buffer
object.
Hierarchy
TComponent (VCL)
7
! TApdBaseComponent (OOMisc) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
" TAdTerminalEmulator (ADTrmEmu). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253
8
9
10
11
TAdTTYEmulator (ADTrmEmu)
Properties
" Buffer
" NeedsUpdate
" CharSetMapping
" Parser
" KeyboardMapping
" Terminal
! Version
Methods
12
13
" BlinkPaint
" KeyDown
" LazyPaint
" GetCursorPos
" KeyPress
" Paint
14
15
16
17
262 Chapter 8: The Terminal Components
1
1
The TAdVT100Emulator Class
1
The TAdVT100Emulator class emulates a Digital Equipment Corporation (DEC) VT100
terminal. It also supports some common extensions to the normal VT100 escape sequence
set, namely support for multiple colors and support for inserting, erasing and deleting
characters.
2
The TAdVT100Emulator class provides support for the standard VT100 terminal modes.
These are as follows:
3
4
• Line feed/newline: whether the line feed character inserts a new line or merely
advances the cursor to the next line with a possible scroll.
• Cursor key mode: whether the cursor movement keys send application mode
sequences or cursor mode sequences.
• ANSI/VT52 mode: whether the terminal interprets ANSI escape sequences or the
restricted VT52 sequences.
• Column mode: whether the terminal displays 80 or 132 characters across.
6
7
8
• Scrolling mode: whether the terminal jump scrolls or smooth scrolls. Although the
VT100 emulator maintains this setting, all scrolling is performed with jump scrolls.
9
• Screen mode: whether the display is normal or reverse-imaged.
• Origin mode: whether the home position for the cursor obeys the current scrolling
region or not.
• Wraparound mode: whether the terminal wraps the cursor to column 1 of the next
line when a character is displayed in the final column.
• Auto repeat: whether keys auto-repeat or not when held down.
• Interface mode: whether the terminal displays with 240 or 480 scanlines. Although
the VT100 emulator maintains this setting, it does not perform any action when it
changes.
• Graphic processor option: whether the terminal uses its GPO. Although the VT100
emulator maintains this setting, it does not perform any action when it changes.
10
11
12
13
14
15
• Keypad mode: whether keys on the numeric keypad send numeric characters or
16
escape sequences.
17
The TAdVT100Emulator Class 263
1
1
1
2
3
4
There are several escape sequences sent by the host computer to which the VT100 terminal
must respond. The VT100 emulator sends the responses shown in Table 8.2.
Table 8.2: VT100 emulator escape sequence responses
Request
Response
Cursor position report
The position of the cursor (<Esc>[ row ; col R).
Status report
Terminal OK (<Esc>[0n).
What Are You?
Base VT100, no options (<Esc>[?;0c).
VT52 Identity request
VT100 acting as VT52 (<Esc>/Z).
5
Hierarchy
6
7
TComponent (VCL)
! TApdBaseComponent (OOMisc) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
" TAdTerminalEmulator (ADTrmEmu). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253
8
9
TAdVT100Emulator (ADTrmEmu)
Properties
ANSIMode
10
AppKeyMode
AppKeyPadMode
11
12
13
14
15
" KeyboardMapping
LEDs
" NeedsUpdate
AutoRepeat
" Buffer
NewLineMode
" Parser
" GetCursorPos
" KeyDown
RelOriginMode
" KeyPress
Col132Mode
RevScreenMode
" LazyPaint
SmoothScrollMode
" Paint
GPOMode
Interlace
" Terminal
Methods
" BlinkPaint
" KeyDown
" LazyPaint
" GetCursorPos
" KeyPress
" Paint
17
264 Chapter 8: The Terminal Components
1
WrapAround
" BlinkPaint
" CharSetMapping
16
1
! Version
Reference Section
1
ANSIMode
run-time property
2
property ANSIMode : Boolean
! Determines whether the terminal acts on ANSI or VT52 sequences.
3
The VT100 terminal can act on two different and separate sets of terminal control
sequences. If ANSIMode is True, the parser will only interpret ANSI escape sequences, and
VT52 sequences are ignored. If False, the parser only understands the restricted VT52
command sequence set and all ANSI escape sequences are ignored.
4
It is the host computer that determines this mode. Although you can change this property it
is inadvisable to do so: bizarre displays may result.
6
AppKeyMode
run-time property
7
property AppKeyMode : Boolean
! Determines which sequences are sent for the cursor movement keys.
The VT100 terminal can send two different escape sequences for the cursor movement keys
(the arrow keys). DEC documentation calls them application mode and cursor key mode.
It is the host computer that determines this mode. Although you can change this property it
is inadvisable to do so: it may result in the host computer not understanding keystrokes.
AppKeypadMode
run-time property
8
9
10
11
property AppKeypadMode : Boolean
! Determines which sequences are sent for the numeric keypad.
The VT100 terminal can send two different escape sequences for the numeric keypad
(including the PF keys). DEC documentation calls them application keypad mode and
numeric keypad mode.
It is the host computer that determines this mode. Although you can change this property it
is inadvisable to do so; it may result in the host computer not understanding keystrokes.
12
13
14
15
16
17
The TAdVT100Emulator Class 265
1
1
1
AutoRepeat
run-time property
property AutoRepeat : Boolean
2
3
! Determines whether held down keys auto-repeat.
The VT100 terminal supports turning off auto-repeat for keys held down on the keyboard.
In general, auto-repeat is on.
On the VT100 the user most often sets this mode, not the host computer.
4
5
Col132Mode
run-time property
property Col132Mode : Boolean
! Determines whether to display 80 or 132 characters across the display.
6
7
The VT100 terminal can support two display widths: 80 characters or 132 characters across.
If Col132Mode is True, the terminal is displaying at 132-column resolution. If False, the
norm, the terminal is displaying at 80-column resolution.
8
It is the host computer that determines this mode. Although you can change this property it
is inadvisable to do so: it may result in bizarre displays since the host is assuming something
that is no longer true.
9
GPOMode
10
11
12
property GPOMode : Boolean
! Determines whether the graphics processor option is active.
Although maintained, the VT100 emulator does nothing with this property. Note that the
VT100 emulator will respond to a “What are you?” request with a “basic VT100 with no
options” reply, which disables any possibility of using the graphics processor option.
Interlace
13
14
15
! Determines whether to use 240 or 480 scanlines.
The VT100 terminal can display using 240 scanlines (True) or 480 (False). Although
maintained, the VT100 emulator does nothing with this property.
17
266 Chapter 8: The Terminal Components
1
run-time property
property Interlace : Boolean
16
1
run-time property
LEDs
read-only, run-time property
1
property LEDs : Integer
! Returns the state of the LEDs on the VT100 keyboard.
2
The VT100 keyboard has a set of four LEDs embedded in the face of the keyboard. The host
computer can set them on or off if required. The LEDs property returns an integer value as a
binary representation of the current state of the LEDs: if the first LED is on, bit 0 is set in the
property value; if the second LED is on, bit 1 is set and so on.
3
4
The LEDs are only visual, they do not affect the use of the keyboard or functionality of the
terminal at all. Indeed, the host computer cannot even read their state.
NewLineMode
run-time property
6
property NewLineMode : Boolean
! Determines the action of a line feed character.
A line feed character (hex 10) received by a VT100 terminal can do one of two things. If this
property is False, the cursor is advanced one row down, keeping in the same column,
scrolling the display up if necessary. The Enter key sends a single <CR> character.
7
8
If this property is True, the cursor is advanced one row down, moving to the first column,
scrolling the display up if necessary. The Enter key sends a pair of characters: <CR><LF>.
9
It is the host computer that determines this mode. Although you can change this property it
is inadvisable to do so: it may result in bizarre displays since the host is assuming something
that is no longer true.
10
RelOriginMode
run-time property
property RelOriginMode : Boolean
11
12
! Determines where the home position is to be found.
If this property is False, the cursor home position is at the top left corner of the display, even
if a scrolling region has been defined. Rows and columns are counted from this origin (this
can be viewed as absolute position mode). Rows and columns are always counted from 1.
Cursor position commands can act outside the active scrolling region.
13
14
15
16
17
The TAdVT100Emulator Class 267
1
1
1
2
If this property is True, the cursor home position is at the top left corner of the scrolling
region. Rows and columns are counted from this origin (this can be viewed as relative
position mode—positions are relative to the scrolling region). Rows and columns are always
counted from 1. Cursor position commands only act inside the active scrolling region.
3
It is the host computer that determines this mode. You can change this property to write text
outside of the scrolling region, but it is advisable to change it back immediately; otherwise, it
may result in bizarre displays since the host is assuming something that is no longer true.
4
RevScreenMode
run-time property
property RevScreenMode : Boolean
5
! Determines whether the display is in reverse image or not.
6
If this property is False, normal text is shown as white on black. If True, normal text is shown
in reversed: as black on white.
7
On the VT100 the user most often sets this mode, not the host computer, although the host
can change the mode if it wishes.
8
SmoothScrollMode
run-time property
property SmoothScrollMode : Boolean
9
! Determines whether scrolling is smooth or jumps.
10
The VT100 terminal can scroll the display in two modes: a smooth scroll, or a jump scroll.
Although maintained, the VT100 emulator does nothing with this property.
11
WrapAround
run-time property
property WrapAround : Boolean
12
! Determines whether displayed text wraps at the right margin.
13
If this property is False, text received when the cursor is at the right hand side does not cause
the cursor to be moved to the first column of the next row.
14
If this property is True, text received when the cursor is at the right hand side will cause the
cursor to be moved to the first column of the next row to display all the text.
15
It is the host computer that determines this mode. It is inadvisable to change this mode,
otherwise it may result in bizarre displays since the host is assuming something that is no
longer true.
16
17
268 Chapter 8: The Terminal Components
1
1
The TAdTerminal Component
1
The TAdTerminal component represents the visual part of a terminal. It is the only visual
component in the Async Professional family of terminal classes. It is responsible for
maintaining the window handle and for performing the low-level processing to get all the
possible keystrokes available on a PC keyboard.
2
Apart from this, it performs next to no work itself, handing off most of the display and other
capabilities to an emulator component (a descendant of TAdTerminalEmulator). Essentially,
the terminal component acts as a conduit between the PC screen and keyboard and the
other classes that perform all of the work.
3
4
The main behaviors of the terminal component and its associated objects are as follows:
• Receives characters from the serial device and passes them onto the emulator for
6
interpretation and processing.
• Traps all keystrokes and passes them onto the emulator for conversion into their
7
terminal equivalents.
• The emulator maintains a buffer of several matrices, one for each of text, background
color, text color, attributes, and character sets. This buffer stores the information
needed to show the visual representation of the terminal.
• The terminal has the capability of maintaining a scrollback buffer to enable the user
8
9
to scroll back through old data that has scrolled off the display. The terminal will
automatically show scrollbars in that case.
10
• The terminal will automatically display scrollbars if the client window is smaller than
11
the terminal display.
• If the terminal component is dropped onto a form and there is no existing emulator
component on that form, the terminal will create an internal instance of a TTY
(teletype) emulator. This means that the terminal component can be used as is for
simple tasks.
In order for the terminal’s window to show data, there are two objects that must be
connected to it: the COM port (a TApdComPort component), and an emulator (a
TAdTerminalEmulator descendant component). To use a terminal component, you would
drop a terminal, a COM port, and a terminal emulator onto the form and connect them up
by setting the ComPort and Emulator properties of the terminal. Once they are connected
up in this fashion, you can set the terminal’s Active property to True and start using the
terminal.
12
13
14
15
16
17
The TAdTerminal Component 269
1
1
1
2
3
4
5
6
7
8
9
The COM port provides the terminal with incoming bytes from a serial communications
device, such as a serial port or Winsock layer. The terminal, in turn, provides the data that
must be output to the serial device. In and of itself, the terminal component doesn’t know
what to do with the incoming data; it has no built in knowledge of terminal control
sequences and how to separate the text from them. Hence, it passes the incoming data
stream directly to the terminal emulator component. This component in turn would use a
parser object to identify terminal control commands, a buffer object to store the displayable
data, and so on. The terminal would also pass all keyboard input to the emulator as well, so
that the emulator can process the keystrokes and determine what to pass back to the host (it
would use a keyboard mapping object to do this). The emulator will use the COM port of
the terminal component to pass back data through the serial device to the host computer.
As for display, the emulator would assume control over the window handle of the terminal
component and draw directly onto its canvas.
The reason for this more complex design—rather than rolling the emulator and terminal
components into one—is flexibility. With this design it is possible to switch emulators on the
fly without having to destroy the window handle used by the terminal and thus avoiding
flicker. Hence, you can easily start off with a TTY emulator for the terminal, and switch to a
VT100 emulator later on. The terminal component thus stores generic information about a
terminal (e.g., the number of characters across and down), whereas the emulator
component holds specific information about a specific terminal (e.g., whether to accept
VT52 or ANSI sequences for a VT100 terminal).
10
11
12
13
14
15
16
17
270 Chapter 8: The Terminal Components
1
1
Figure 8.2 shows the interrelationship between the client window, the terminal display and
the scrollback buffer.
1
2
Columns
3
ROW
SCROLLBACK
BUFFER
ScrollbackRows
ClientOrigin
4
6
7
-1
0
1
2
TERMINAL
DISPLAY
8
9
ClientCols
ClientRows
10
Rows
11
TERMINAL
CLIENT
WINDOW
12
Figure 8.2: Interrelationship between the client window, the terminal display, and the scrollback buffer.
The client window acts as a viewport on the terminal display. In general, you would make
sure that the client window was large enough to display all of the data in the terminal display.
If that is so, no scroll bars are shown. Should the client window be smaller vertically, or
horizontally, or both, than the terminal display, the relevant scrollbars are automatically
shown to enable the user to scroll though and view the entire terminal display.
13
14
15
16
17
The TAdTerminal Component 271
1
1
1
2
3
4
5
If scrollback mode is activated, the client window acts as a viewport on the entire scrollback
buffer, not just on the terminal display. Scrollbars would automatically be shown, if
required, to enable the user to scroll through the entire buffer. In scrollback mode, the
terminal component no longer processes any data from the serial device nor does it trap
keyboard messages and pass them on to the emulator.
The terminal component also supports a lazy write mode. In this mode, incoming data is
processed but not displayed until one of two conditions occurs: either a certain number of
bytes is received, or a certain amount of time has elapsed. Once the required time has gone
by or the number of bytes has been received, the terminal component will force its display to
be updated, and the process starts all over again. Fine tuning of these two properties will
result in a terminal that is more responsive, and has more efficient and smoother behavior.
Font handling
6
7
8
9
10
11
12
13
The issue of fonts becomes complex when dealing with a terminal that should support
displaying non-standard glyphs for characters. For example, when emulating the VT100
terminal, the standard Windows fixed pitch fonts do not have every single glyph. The
CharSet mapping class attempts to ameliorate the situation by providing a mapping from a
character in a character set to a glyph in a Windows font. Apart from the simple TTY
emulation, there will unfortunately be several fonts in action for the usual terminal
emulations. The ability to switch fonts easily as with simple components cannot apply in the
case of the terminal. The terminal’s situation is entirely more complex: text displayed may
contain glyphs from several unrelated fonts.
What the terminal component and its associated classes attempt to do is to make the
displayed glyphs the same point size. The point size of the fonts used by the terminal is taken
to be the same as the Size property of its Font property. Hence, to change the size of the text
displayed by the terminal, you would need to change the Font.Size property. For certain
emulations, the terminal’s Font property will in fact determine the font with which the text is
displayed—in other words, there is no CharSet mapping required.
The terminal classes will allow the use of proportional fonts. However, it should be noted
that text displayed using these proportional fonts will be shown in fixed size character cells,
and proportional fonts displayed in fixed pitch can look awkward and clunky. Our advice is
to try and stick to fixed pitch fonts wherever possible.
14
15
16
17
272 Chapter 8: The Terminal Components
1
1
Capturing data
The terminal component has a built-in facility to capture data, that is, to write all data
received by the serial port to a capture file. The capture file can be used for later review of an
on-line session.
The data is written to the capture file before the emulator has an opportunity to see the data
stream and to parse out the terminal control sequences. Hence, the capture file is not a
simple text file, it could consist of numerous control characters and escape sequences as well
as normal text.
1
2
3
4
Characters typed at the keyboard are not saved in the capture file. However, such characters
usually end up in the capture file anyway, since the host computer echoes them back to the
terminal.
Using the clipboard
The terminal window provides support for copying displayed text to the Windows
clipboard. Because the text in a terminal window is read-only, cutting and pasting are not
supported. Block marking using the cursor keys is also not supported by the terminal
window, since it implies that you can move the caret and in general it is the remote computer
that controls the location of the caret.
6
7
8
The terminal window does support the following:
9
• Normal mode block marking using the mouse.
• Scrollback mode block marking using the mouse.
10
• Copying the marked block to the clipboard.
When in normal mode, only the visible contents of the terminal window can be marked.
When in scrollback mode, the visible contents can be marked and, by moving the cursor
above or below the window, the window can be scrolled to allow any part of the scrollback
buffer to be marked.
11
12
13
14
15
16
17
The TAdTerminal Component 273
1
1
1
2
3
4
5
You can copy the marked block to the clipboard by calling the terminal window routine
CopyToClipboard.
" Caution: Although the CopyToClipboard method will copy the marked text to the
clipboard, you should be aware that what you see may not be what you get. The terminal
classes in Async Professional support the notion of different character sets. In other words,
with certain emulations, the glyphs that are displayed on the terminal display may seem to
have no connection with the characters that are actually there. For example, with a VT100
terminal, when using the USASCII character set the character ‘m’ will be displayed as a
lowercase ‘m’. However, when using the special graphics character set, the character ‘m’ is
rendered as the lower left corner of the line draw set (the glyph that looks like an ‘L’). The
problem is that, when you copy the marked text to the clipboard, you will lose the character
set definition for each character. Hence, you will just get the character ‘m’ in the clipboard
and not know whether it really was shown as an ‘m’ or some other glyph.
6
7
8
9
10
11
12
13
14
15
16
17
274 Chapter 8: The Terminal Components
1
1
Hierarchy
1
TWinControl (VCL)
! TApdBaseWinControl (OOMisc) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
TAdTerminal (ADTrmEmu)
2
3
Properties
Active
CharWidth
Line
Attributes
Columns
Rows
BackColor
ComPort
ScrollbackRows
BlinkTime
Emulator
Scrollback
Capture
ForeColor
CaptureFile
HalfDuplex
CharHeight
LazyByteDelay
CharSet
LazyTimeDelay
UseLazyDisplay
4
6
! Version
WantAllKeys
7
8
Methods
Clear
CreateWnd
DestroyWnd
ClearAll
Create
WriteChar
CopyToClipboard
Destroy
WriteString
Events
9
10
11
OnCursorMoved
12
13
14
15
16
17
The TAdTerminal Component 275
1
1
1
Reference Section
Active
property
2
property Active : Boolean
3
Default: True
! Determines whether the terminal is accepting serial events.
4
5
6
7
8
9
10
11
12
13
Setting Active to True causes the terminal to start processing serial and keyboard data and to
display this information in the terminal window. At run time, if an attempt is made to set
Active True without a serial port component being assigned to the ComPort property, or
without an emulator component being assigned to the Emulator property, the attempt is
ignored. It is acceptable to set Active to True at design time; this causes the terminal to start
processing events automatically when the form is created at run time.
You must set Active to False if the terminal is being used in combination with another
component, such as a file transfer component, that needs exclusive access to some or all of
the data stream. During a file transfer, set Active to False for the duration of the transfer and
True when the transfer is over and data should appear in the terminal window again.
The following example sets Active to False as it starts receiving a file to prevent the terminal
from displaying the received data. An OnProtocolFinish event re-enables the terminal
window once the protocol informs the application that the transfer is complete.
AdTerminal1.Active := false;
ApdProtocol1.StartReceive;
...
procedure TMyForm.ProtocolFinish(
CP : TObject; ErrorCode : Integer);
begin
AdTerminal1.SetFocus;
AdTerminal1.Active := true;
end;
See also: ComPort, Emulator
14
15
16
17
276 Chapter 8: The Terminal Components
1
1
Attributes
run-time, array property
1
property Attributes[aRow, aCol : Integer] : TAdTerminalCharAttrs
TAdTerminalCharAttr = (tcaBold, tcaUnderline, tcaStrikethrough,
tcaBlink, tcaReverse, tcaInvisible);
2
TAdTerminalCharAttrs = set of TAdTerminalCharAttr;
3
! Accesses the attributes of text in the display.
Attributes enables the direct manipulation of the attributes for characters displayed by the
terminal. Attributes is an array property indexed by a combination the row number (aRow)
and the column number (aCol). Both aRow and aCol are one-based: the home position of
the terminal display is at row 1 column 1. Note, however, that if you have a scrollback buffer
that aRow can take on negative values, as well, to identify non-visible rows in the scrollback
buffer.
The result value is a set of possible attributes. They are tcaBold for bold text; tcaUnderline
for underlined text; tcaStrikethrough for text that has a line through it, as if it had been
deleted (this text is struck through); tcaBlink for blinking text; tcaReverse for reversed text;
and tcaInvisible for text that is not visible.
4
6
7
8
The following example sets all text on row 5 to blinking:
for I := 1 to AdTerminal1.Columns do begin
AdTerminal1.Attributes[5, I] :=
AdTerminal1.Attributes[5, I] + [tcaBlink];
However, do notice that this direct manipulation is fairly inefficient.
See also: BackColor, CharSet, ForeColor
9
10
11
12
13
14
15
16
17
The TAdTerminal Component 277
1
1
1
BackColor
run-time, array property
property BackColor[aRow, aCol : Integer] : TColor
2
3
4
5
6
! Accesses the background color of text in the display.
BackColor enables the direct manipulation of the background color for characters displayed
by the terminal. BackColor is an array property indexed by a combination the row number
(aRow) and the column number (aCol). Both aRow and aCol are one-based: the home
position of the terminal display is at row 1 column 1. Note, however, that if you have a
scrollback buffer that aRow can take on negative values, as well, to identify non-visible rows
in the scrollback buffer.
However, do notice that this direct manipulation is fairly inefficient.
See also: Attributes, CharSet, ForeColor
BlinkTime
7
8
9
10
11
12
property BlinkTime : Integer
Default: 500
! Defines the time in milliseconds between cycles for blinking text.
Some terminals enable text displayed by the terminal to be blinking. The BlinkTime
property defines the elapsed time for a full cycle for the text being displayed, being invisible,
and being displayed again.
Note that, to provide this functionality, the terminal sets up a timer to tick at this rate. A
Windows timer is low-priority; if the PC is performing other work, it will seem as if the
blinking text has either stopped blinking or has disappeared completely. Also, if you set the
BlinkTime property too low, the terminal and emulator will spend most of their time
updating the window, especially if there’s a lot of blinking text.
Capture
13
14
property
property
property Capture : TAdCaptureMode
TAdCaptureMode = (cmOff, cmOn, cmAppend);
Default: cmOff
15
16
! Defines whether the data received by the terminal is captured to file.
The Capture property has only two values on reading: whether the terminal is capturing
data (cmOn will be returned) or not (cmOff will be returned).
17
278 Chapter 8: The Terminal Components
1
1
It has three possible values on writing: cmOn, cmOff, or cmAppend. If the value written is
cmAppend and the current value is cmOff, the capture file is opened in non-sharing mode
for appending data and the value of the Capture property is then set to cmOn. Note that if
the file doesn’t exist at the time the property is set, it will be created. If the value written is
cmAppend and the current value is cmOn, the assignment is ignored and nothing happens.
1
2
If the value written is cmOn and the current value is cmOff, the file is created. If it existed
prior to the assignment, it will be overwritten.
3
The name of the file where captured data is written is given by the CaptureFile property.
4
All data coming into the terminal is written to the file without any effort being made to parse
it or identify terminal control sequences. Thus for a complex terminal emulation the data in
the capture file will consist of intermingled text and terminal control sequences.
If the CaptureFile property has not been set to the name of a file (i.e., it is the empty string),
setting Capture to cmOn or cmAppend will have no effect. The attempt will be ignored and
no exception will be raised. If the CaptureFile property has been set, an attempt is made to
create or open the file so named. This operation can of course fail for any of a number of
different reasons. Internally, the terminal uses a file stream—TFileStream—to access the
file, so any I/O exceptions raised will be those used by the standard Delphi class.
6
7
8
See also: CaptureFile
CaptureFile
property
property CaptureFile : string
9
10
Default: “APRO.CAP”
! Defines the name of the file where the terminal writes captured data.
It is possible to change the name of the file where captured data is sent while data is being
captured. Internally, the terminal component sets the Capture property to cmOff, changes
the value of the CaptureFile property to the new filename, and then sets the Capture
property to cmOn again. This does mean that the file named by the new value of CaptureFile
is created afresh: the old file, if it exists, is overwritten. If you wish to append to the file with
the new name, you will need to manually set Capture to cmOff, set the value of CaptureFile
to the new filename, and then set Capture to cmAppend.
11
12
13
14
See also: Capture
15
16
17
The TAdTerminal Component 279
1
1
1
CharHeight
read-only, run-time property
property CharHeight : Integer
2
3
4
5
6
7
8
9
10
11
12
13
14
15
! Defines the height in pixels of a character cell for the terminal.
The terminal display can be viewed as a matrix of fixed-sized character cells. There are Rows
cells vertically and Columns cells horizontally. The size of these character cells is calculated
as the maximum required to display all of the possible characters in all of the possible
character sets. Since different characters in different character sets could be displayed using
different Windows fonts, the character cell size can be viewed as a function of the different
possible Windows fonts used by the emulator. The value of CharHeight is the maximum
needed to display any glyph.
The overall size of the characters displayed by the terminal is given by the Size property of
the Font property of the terminal component. The Font property is used as the default font
for those terminal emulations that don’t support character sets. For terminals that do
support character sets, the size of the glyphs is given by the Font.Size value.
See also: CharWidth
CharSet
run-time, array property
property CharSet[aRow, aCol : Integer] : Byte
! Accesses the character set of text in the display.
The Async Professional terminal supports the notion of different character sets for different
text on the display. Normally the text is shown in one character set only, but in certain cases
(for example, the line draw glyphs with the VT100 terminal) the glyphs shown on the
terminal are drawn from other character sets. Rather than define an enumerated type or
class for each character set used by the terminal, character sets are identified by an
anonymous byte value. It is up to the emulator to define which byte value represents which
character set.
CharSet enables the direct manipulation of the character set for characters displayed by the
terminal. CharSet is an array property indexed by a combination the row number (aRow)
and the column number (aCol). Both aRow and aCol are one-based; the home position of
the terminal display is at row 1 column 1. Note, however, that if you have a scrollback buffer
that aRow can take on negative values, as well, to identify non-visible rows in the scrollback
buffer.
However, do notice that this direct manipulation is fairly inefficient.
16
See also: Attributes, BackColor, ForeColor
17
280 Chapter 8: The Terminal Components
1
1
CharWidth
read-only, run-time property
1
property CharWidth : Integer
! Defines the width in pixels of a character cell for the terminal.
2
The terminal display can be viewed as a matrix of fixed-sized character cells. There are Rows
cells vertically and Columns cells horizontally. The size of these character cells is calculated
as the maximum required to display all of the possible characters in all of the possible
character sets. Since different characters in different character sets could be displayed using
different Windows fonts, the character cell size can be viewed as a function of the different
possible Windows fonts used by the emulator. The value of CharWidth is the maximum
needed to display any glyph.
The overall size of the characters displayed by the terminal is given by the Size property of
the Font property of the terminal component. The Font property is used as the default font
for those terminal emulations that don’t support character sets. For terminals that do
support character sets, the size of the glyphs is given by the Font.Size value.
See also: CharHeight
Clear
method
procedure Clear;
3
4
6
7
8
9
! Clears the terminal display.
To clear the display, the terminal component will internally scroll the window up by Rows
lines. This means that the current display will scroll into the non-visible part of the
scrollback buffer and can be viewed in scrollback mode. The top lines of the scrollback
buffer will of course disappear, being completely scrolled off the top of the buffer.
(The Clear method is the equivalent of the ClearWindow method of the deprecated
TApdTerminal.)
See also: ClearAll
10
11
12
13
14
15
16
17
The TAdTerminal Component 281
1
1
1
ClearAll
method
procedure ClearAll;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
! Clears the entire scrollback buffer including the terminal display.
To clear the scrollback buffer, the terminal sets all characters in the buffer to the space
character; all attribute values to the empty set (equivalent to “normal” text); all background
color values to the Color property; all foreground color values to Font.Color; all charset
values to 0.
(The ClearAll method is the equivalent of the ClearBuffer method of the deprecated
TApdTerminal.)
See also: Clear
Columns
property Columns : Integer
Default: 80
! Defines the number of columns across the terminal display.
The value of the Columns property is the number of standard-sized characters that can be
written across the terminal display. In general, it is a value like 80, but in certain
circumstances it could be 132. If the original terminal supports double-width characters, the
value of Columns reflects that for standard-sized characters, not the double-width ones.
Altering the value of Columns will cause the underlying buffer to be resized. The terminal
will attempt to save as much of the original data as possible during the resize operation.
Setting the value of Columns to less than that supported by the original terminal itself is
liable to produce funny looking displays, since the host computer will assume that the
terminal is the correct size and position text accordingly.
If the host computer switches the terminal into a mode with a different number of columns
(say, from 80 to 132 characters across), the value of Columns will change to reflect that
switch.
" Caution: Because of the structure of the buffer class, in Delphi 1, the product of Columns
and ScrollbackRows cannot exceed 16,383. For an 80 column display, that means the
number of scrollback rows cannot exceed 204. There is no such limitation with 32-bit
compilers.
16
17
282 Chapter 8: The Terminal Components
1
1
property
ComPort
property
1
property ComPort : TApdCustomComPort
! Defines the serial device to which the terminal is connected.
2
ComPort is usually set automatically at design time to the first TApdCustomComPort
descendant component the terminal component finds on the form. If necessary, use the
Object Inspector to select a different TApdCustomComPort descendant component.
Setting the ComPort property at run time is necessary only when using a dynamically
created TApdCustomComPort object or when selecting among several
TApdCustomComPort descendant components.
3
4
Note that setting the value of ComPort may not be enough to get the terminal to display
data. You must also activate the terminal by ensuring that Active is set to True, and you must
also supply a terminal emulator component by setting the Emulator property to process the
incoming data stream.
6
See also: Active, Emulator
7
CopyToClipboard
method
8
procedure CopyToClipboard;
9
! Copies the marked block to the clipboard.
This routine copies the currently marked block to the Windows clipboard. The block is
copied in CF_TEXT format, where a carriage return/line feed follows each line.
10
Please read the caution within the section entitled “Using the clipboard” on page 273 in the
introduction to the TAdTerminal component. This discusses why, after calling this method,
the clipboard may not contain what you see on the display.
11
Create
constructor
constructor Create(aOwner : TComponent); override;
!
12
13
Creates an instance of the terminal component.
The constructor will also create an instance of a teletype emulator (TTY emulator) for use
when the Emulator property is not set. This ensures that the terminal at least functions as a
TTY terminal without any extra work.
14
15
See also: Destroy
16
17
The TAdTerminal Component 283
1
1
1
CreateWnd
method
procedure CreateWnd; override;
2
3
! Creates the window handle for the terminal component.
Once the window handle has been created, this method installs a keyboard hook in order to
trap all keystrokes made by the user.
See also: DestroyWnd
4
5
Destroy
destructor
destructor Destroy; override;
! Destroys an instance of the terminal component.
6
7
The destructor will free all memory and resources allocated by the instance. Of particular
note is that if data is being captured when Destroy is called, the capture file is closed
properly first.
See also: Create
8
9
DestroyWnd
method
procedure DestroyWnd; override;
! Destroys the window handle of the terminal component.
10
If a keyboard hook was properly installed by the CreateWnd procedure, DestroyWnd will
remove it before destroying the window handle associated with the terminal component.
11
See also: CreateWnd
12
Emulator
property
property Emulator : TAdTerminalEmulator
13
14
15
! Defines the terminal emulator that processes incoming data.
The terminal component is merely a conduit between the serial device defined by the
ComPort property and the terminal emulator defined by Emulator. The terminal
component also provides the window handle, and hence, the canvas, that the emulator will
use to draw the terminal display.
16
17
284 Chapter 8: The Terminal Components
1
1
The emulator processes the input data stream looking for terminal control sequences. It will
then use the terminal component’s window handle to paint a representation of the terminal
screen. The terminal component also traps keystrokes and passes them to the emulator. The
emulator will then convert these keystrokes into valid messages or sequences for the host
computer and use the terminal’s ComPort to pass them back.
The Emulator property is usually set automatically at design time to the first
TAdTerminalEmulator descendant component the terminal component finds on the form. If
necessary, use the Object Inspector to select a different TAdTerminalEmulator descendant
component.
Usually, setting the Emulator property at run time is necessary only when switching
between different terminal emulations. The terminal does not clear the display at such
times, it is up to the new terminal emulator to reset its buffer and to display the cleared
screen.
Note that setting the value of Emulator may not be enough to get the terminal to display
data. You must also activate the terminal by ensuring that Active is set to True, and you must
also supply a COM port component by setting the ComPort property to an appropriate
TApdCustomComPort component.
2
3
4
6
7
8
See also: Active, ComPort
ForeColor
1
run-time, array property
9
property ForeColor[aRow, aCol : Integer] : TColor
10
! Accesses the foreground color of text in the display.
ForeColor enables the direct manipulation of the foreground color (the color of the glyphs
themselves) for characters displayed by the terminal. ForeColor is an array property indexed
by a combination the row number (aRow) and the column number (aCol). Both aRow and
aCol are one-based: the home position of the terminal display is at row 1 column 1. Note,
however, that if you have a scrollback buffer that aRow can take on negative values, as well,
to identify non-visible rows in the scrollback buffer.
11
12
13
See also: Attributes, BackColor, CharSet
14
15
16
17
The TAdTerminal Component 285
1
1
1
HalfDuplex
property
property HalfDuplex : Boolean
2
Default: False
! Determines whether local data is echoed to the terminal display.
3
4
5
If HalfDuplex is False (the default), data entered at the local keyboard is displayed only if the
remote host computer echoes it back.
If HalfDuplex is True, data entered at the local keyboard is automatically displayed in the
terminal window. If the host computer is echoing the input data back as well, each character
is displayed twice.
LazyByteDelay
6
7
property
property LazyByteDelay : Integer
Default: 128
! Determines the number of bytes received before the display is forcibly repainted.
8
9
10
11
The TAdTerminal supports a lazy writing mode. When this mode is active, rather than
update the display every time a new character appears from the serial device, the terminal
and its associated classes will only display new data after a certain amount of time, or after a
certain number of bytes have been received, or both. This gives the terminal a more efficient
and smoother feel. The value of LazyByteDelay defines how many bytes must be received
before the terminal window is updated. The default value is a compromise between
efficiently handling the display and providing timely visual feedback to the user.
See also: LazyTimeDelay, UseLazyWrite
LazyTimeDelay
12
13
property
property LazyTimeDelay : Integer
Default: 250
! Determines the number of elapsed milliseconds before the display is forcibly repainted.
14
15
16
17
The TAdTerminal supports a lazy writing mode. When this mode is active, rather than
update the display every time a new character appears from the serial device, the terminal
and its associated classes will only display new data after a certain amount of time, or after a
certain number of bytes have been received, or both. This gives the terminal a more efficient
and smoother feel. The value of LazyTimeDelay defines how many milliseconds must pass
before the terminal window is updated. The default value is a compromise between
efficiently handling the display and providing timely visual feedback to the user.
See also: LazyByteDelay, UseLazyWrite
286 Chapter 8: The Terminal Components
1
1
Line
run-time, array property
1
property Line[aRow : Integer] : string
! Accesses the text data for a row in the display.
2
Line returns and sets the characters that make up a row in the terminal. It enables the direct
manipulation of the text displayed by the terminal. Line is an array property indexed by the
row number (aRow). aRow is one-based: the home position of the terminal display is at row
1 column 1. Note, however, that if you have a scrollback buffer that aRow can take on
negative values, as well, to identify non-visible rows in the scrollback buffer.
3
4
However, do notice that this direct manipulation is fairly inefficient.
" Caution: Line returns the character values that make up a row. For some terminals, the
glyph you see on the terminal for a particular character value is not only based on the
character value itself, but also on the character set that is being used to display that character.
For example, on a VT100 terminal, if the character ‘m’ is displayed using the USASCII
character set, you will see the usual lower case ‘m’ glyph on the display. However, if the same
character ‘m’ is displayed using the Special Characters character set, you will see the lower
left corner linedraw glyph (the one that looks like an ‘L’). All the Line property will return is
the ‘m’ character value at that particular column position.
See also: Attributes, BackColor, CharSet, ForeColor
Rows
6
7
8
9
property
10
property Rows : Integer
Default: 24
11
! Defines the number of rows down the terminal display.
The value of the Rows property is the number of standard-sized characters that can be
written vertically on the terminal display. In general, it is a value like 24 or 25. If the original
terminal supports double-height characters then the value of Rows still reflects that for
standard-sized characters, not the double-height ones.
12
13
14
15
16
17
The TAdTerminal Component 287
1
1
1
2
3
4
Notice that Rows is the number of rows on the original terminal, not the number of rows in
the scrollback buffer. The terminal display will consist of Rows lines, with Columns
characters in each.
Altering the value of Rows may cause the underlying buffer to be resized. The terminal will
attempt to save as much of the original data as possible during the resize operation. The data
will be preserved from the bottom of the terminal upwards. In other words, if you reduce the
number of rows from 20 to 15, say, you will see the bottom 15 rows of the original display
after the operation completes, not the top 15.
5
Setting the value of Rows to less than that supported by the original terminal itself is liable to
produce funny looking displays, since the host computer will assume that the terminal is the
correct size and position text accordingly.
6
The rows in the terminal display are counted from 1, with the top row of the terminal being
row 1.
7
Scrollback
property
property Scrollback : Boolean
8
Default: False
! Defines whether the terminal is in scrollback mode.
9
10
11
12
13
14
If Scrollback is set True, the terminal is placed into scrollback mode. In this mode,
keystrokes are no longer translated into their terminal equivalents and instead serve to
navigate through the scrollback buffer. Hence, in scrollback mode, the Page Up/Page Down
keys will move the user through the scrollback buffer, as will the standard arrow keys.
If scrollback mode is activated, the terminal will also no longer receive data from the serial
device. It is recommended that you impose flow control on the COM port when you switch
into scrollback mode (either send an XOFF character or drop a hardware flow control
signal), to help avoid the dispatcher’s input buffer overflowing. The terminal does not do
this itself. Imposing flow control helps keep the terminal data stable to allow the user to
navigate though the scrollback buffer in a profitable manner. Obviously, when you leave
scrollback mode, you would end the flow control condition to allow more data to come
through and be processed.
See also: ScrollbackRows
15
16
17
288 Chapter 8: The Terminal Components
1
1
ScrollbackRows
property
1
property ScrollbackRows : Integer
2
Default: 200
! Defines the number of rows in the scrollback buffer.
The scrollback buffer consists of the visible part of the terminal display, together with the
previous data that has scrolled off the top of the terminal display. In general you would set
ScrollbackRows such that you could hold four or five screens’ worth of previous data.
The value of the ScrollbackRows property must be greater than or equal to the value of
Rows. If you attempt to set ScrollbackRows to a value less than Rows, the new value is
adjusted to be equal to Rows. No exception is generated in this situation. If the original
terminal supports double-height characters then the value of ScrollbackRows still reflects
that for standard-sized characters, not the double-height ones.
Altering the value of ScrollbackRows may cause the underlying buffer to be resized. The
terminal will attempt to save as much of the original data as possible during the resize
operation. If the value of ScrollbackRows is reduced the data is removed from the top of the
buffer rather than the bottom.
The rows in the terminal display are counted from 1, with the top row of the terminal being
row 1. The rows above the actual terminal display in the scrollback area are counted
backwards from 1. Hence, the row above the top row of the actual terminal display is row 0,
the one above that row –1, and so on.
UseLazyDisplay
3
4
6
7
8
9
10
property
11
property UseLazyDisplay : Boolean
Default: True
! Defines whether the terminal immediately displays new incoming data or not.
12
The TAdTerminal supports a lazy writing mode. When this mode is active, rather than
update the display every time a new character appears from the serial device, the terminal
and its associated classes will only display new data after a certain amount of time, or after a
certain number of bytes have been received, or both. This gives the terminal a more efficient
and smoother feel.
13
If UseLazyDisplay is False, the terminal will display every incoming character as and when it
arrives. If UseLazyDisplay is True, the terminal will display the new data on the screen after
LazyByteDelay bytes have been received since it last updated the window, or after
LazyTimeDelay milliseconds have elapsed.
15
14
16
17
The TAdTerminal Component 289
1
1
1
2
3
The lazy writing mode only applied to data written to the terminal, either from the serial
device, or from the keyboard in half duplex mode, or from data explicitly written from
calling WriteChar or WriteString. If the terminal component’s window is invalidated due to
another window covering it and then being moved, or from the application being
minimized and then restored, the terminal display is immediately repainted.
See also: LazyByteDelay, LazyTimeDelay
WantAllKeys
property
4
property WantAllKeys : Boolean
5
Default: True
! Defines whether the terminal component hooks and retrieves all keystrokes.
6
7
8
9
10
11
12
13
Part of the job of a terminal component is the ability to map the PC keyboard onto a
terminal keyboard. This latter keyboard might be a completely different layout than the PC
keyboard and, apart from the alphabetic key section, have different keys for different host
functions. The keyboard mapping should attempt to match PC keys (whether they are altshifted, ctrl-shifted, or whatever) onto appropriate terminal keys.
A problem that will occur is that keys like F1, F10, Enter, Tab, and so on, have a well-defined
meaning in the Windows world. Normally, controls on a form would ignore these keys since
they have dialog-specific or application-wide meanings. However, for a terminal component
it often makes sense to have these keys perform a terminal related function and to suppress
the standard Windows meaning. If WantAllKeys is True, the terminal component will
attempt to hook and trap all keystrokes generated while it has focus. Hence, for example, F1
will not bring up the help system (it will not cause a WM_HELP message to be sent to the
control), F10 will not activate the main menu of the application, and so on.
If WantAllKeys is False, the terminal component will not perform anything special with
regard to the keyboard. It will just trap WM_KEYDOWN and WM_SYSKEYDOWN
messages and pass them on to the emulator for processing. Standard Windows keys will
perform their usual functions.
14
15
16
17
290 Chapter 8: The Terminal Components
1
1
WriteChar
method
1
procedure WriteChar(aCh : AnsiChar);
! Writes a single character to the terminal.
2
The character written to the terminal will go through the same steps that a character that
had arrived from the serial device would go through. In other words, the character is first
passed to the emulator, which decides what to do with it. If the emulator decides that the
character is part of a terminal control sequence, it would appear as if the character had not
been accepted—it would not appear on the display—when in reality it had.
The terminal will accept a character written with WriteChar at any time, even when it is
actively receiving data from the serial device. Be aware that under these circumstances, the
character written with WriteChar will intermingle with data from the serial device and may
cause some bizarre behavior and displays.
3
4
6
Note also that the lazy write mode still applies to text written to the terminal with WriteChar.
If UseLazyDisplay is True, the text will appear at the appropriate time. You can force the new
data to be displayed in this case by calling the Update method.
7
WriteString
8
method
procedure WriteString(const aSt : string);
9
! Writes a string to the terminal.
The string written to the terminal will go through the same steps that characters that have
arrived from the serial device would go through. In other words, the characters in the string
are first passed to the emulator, which decides what to do with them. If the emulator decides
that certain characters are part of a terminal control sequence, it would appear as if the
string had not been fully accepted—it would not appear on the display—when in reality it
had. You can therefore use WriteString to send terminal control sequences to the terminal to
alter its behavior. Note that the host computer would be unaware of this change in behavior.
10
The terminal will accept a string written with WriteString at any time, even when it is
actively receiving data from the serial device. Be aware that under these circumstances, the
characters written with WriteString will intermingle with data from the serial device and
may cause some bizarre behavior and displays.
13
Note also that the lazy write mode still applies to text written to the terminal with
WriteString. If UseLazyDisplay is True, the text will appear at the appropriate time. You can
force the new data to be displayed in this case by calling the Update method.
11
12
14
15
16
17
The TAdTerminal Component 291
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
292 Chapter 8: The Terminal Components
1
1
Chapter 9: IP Telephony
1
2
Async Professional includes components to transmit data over phone lines and networks,
and components to transmit wave recordings through voice modems, APRO also includes a
component to transmit audio and video through the network through IP Telephony.
3
IP (Internet Protocol) Telephony is a technology where audio and video can be streamed
across a network. As it’s name implies, IP Telephony uses IP as the transport layer. This
transport layer is provided through TCP/IP. IP Telephony, in APRO, also uses TAPI 3.x,
which is only available for Windows 2000 (future Windows versions should also support this
implementation). TAPI 3.x, in turn, uses H.323 and the Microsoft H.323 TAPI Service
Provider, which is installed by default.
4
5
6
IP Telephony includes the transmission of voice and video over the network. The network
can be a local area network (LAN), wide area network (WAN) or the Internet. This
technology offers the capability of establishing real-time conferencing around the globe
using existing network topology. Using this technology you can hold a real-time conference
(complete with audio and video), telecommuting, distance learning, and video on demand,
to name a few of the possibilities.
7
8
9
10
11
12
13
14
15
16
17
293
1
1
1
IP Telephony in Async Professional
2
The TApdVoIP component implements IP Telephony (also known as “Voice over IP”)
through the services of TAPI 3.x. TAPI 3.x is available for Windows 2000. Documentation
on the Microsoft web site confirms that future Windows versions will support the TAPI 3.x
architecture. The TApdVoIP component implements a small subset of TAPI 3.x, limited to
the functionality required for Voice over IP.
3
4
5
6
7
8
9
10
11
12
Since the TApdVoIP component requires TAPI 3.x, and TAPI 3.x is available only on
Windows 2000 and later, the TApdVoIP component requires Windows 2000 or later and the
Microsoft H.323 TAPI Service Provider. An application that contains the TApdVoIP
component can be instantiated on an unsupported Windows version, however all
TApdVoIP methods will raise the EVoIPNotSupported exception. To prevent this exception,
the VoIPAvailable property can be checked, this property will be True if the required TAPI
components are available and False if TAPI does not support it.
H.323
TAPI 3.x implements IP Telephony through the H.323 protocol. This protocol is optimized
for the transmission of voice and video over connectionless networks that do not provide
guaranteed quality of service (such as IP-based networks and the Internet). H.323 is a
comprehensive ITU (International Telecommunications Union) standard for multimedia
communications. This protocol defines call control techniques, multimedia and bandwidth
management, and mandates for standard audio and video codecs. The H.323 protocol is
also platform independent, which means an H.323 instance on a Windows machine can
communicate with an H.323 instance on another operating system.
Detailed discussions, and implementation specifications, are available on the ITU Web site.
Knowledge of H.323 is not required to use the IP Telephony components in Async
Professional. TAPI 3 and APRO will handle all of the details for you.
13
14
15
16
17
294 Chapter 9: IP Telephony
1
1
Configuration for VoIP
1
As previously stated, the Voice over IP functionality in the TApdVoIP component is
available in TAPI 3.x. This TAPI version is available only for Windows 2000, although future
Windows versions will most likely include TAPI 3.x compatibility.
2
The TApdVoIP component also automatically selects the H.323 TAPI device. If this device is
not installed (it is installed by default in Windows 2000), the VoIPAvailable property is set to
False when the component is created, and the TApdVoIP methods will raise the
EVoIPNotSupported exception when called.
The audio and video streaming in VoIP are provided through H.323 media services (also
installed by default in Windows 2000). These media services support selection of the audio
input, audio output, video input and video output devices. When the TApdVoIP component
is created, the media services are queried for supported media terminals. As the terminals
are being queried, their capabilities are also queried. All of the supported media terminals
are stored in the AvailableTerminalDevices property. This property is a TStrings instance,
the Strings value is the name of the terminal; the Objects value is a TApdVoIPTerminal
object. See the TApdVoIPTerminal description later in this chapter for details.
Audio and video device selection
There are four properties available in the TApdVoIP component to select which media
terminal to use. The AudioInDevice property determines which device to use for audio
input (usually a microphone). The AudioOutDevice property determines which device to
use for audio output (usually external speakers or headset speakers). The VideoInDevice
property determines which device to use for video input (usually a digital video camera or
web cam installed on the system). The property values for these properties contain the name
of the selected device. The final property for terminal selection is the VideoOutDevice
property. This property specifies a TWinControl that is used to render the video stream
received through the call. Any TWinControl descendent can be used, although a TPanel is
probably the most applicable.
The default values for these terminal properties are ‘’ (empty string) for the AudioInDevice,
AudioOutDevice and VideoInDevice, and nil for the VideoOutDevice. The default values
disable that media type on the call. For example, if VideoInDevice is an empty string, the
call will not be configured to support video input.
The AudioInDevice, AudioOutDevice and VideoInDevice properties are strings, which
correspond to the DeviceName property of the associated TApdVoIPTerminal object. These
properties can be set directly by assigning the DeviceName of the terminal to the property.
3
4
6
7
8
9
10
11
12
13
14
15
16
17
Configuration for VoIP 295
1
1
1
These properties can also be set through the property editor. This property editor is also
available at run time through the ShowMediaSelectDialog method. The property editor for
these properties contains a combo box for each property as shown in Figure 9.1.
2
3
4
5
Figure 9.1: Media device property editor.
6
8
Each combo box contains the terminal types associated with the given property. The “Audio
input” control contains only those devices that support audio input; the “Audio output”
control contains only those devices that support audio output; the “Video input” control
contains only those devices that support video input. When OK is clicked, the
AudioInDevice, AudioOutDevice and VideoInDevice properties are updated to reflect the
changes. If Cancel is clicked, the changes are disregarded.
9
Originating a VoIP call
7
10
11
12
When originating a call, the TApdVoIP component initializes the underlying TAPI and
H.323 layers, and then attempts to connect to a VoIP implementation elsewhere on the
network. The Connect method of the TApdVoIP component begins the connection attempt.
The destination address is determined by the DestAddr string parameter of the Connect
method. VoIP addresses can be dotted quad Internet addresses or machine names. For
example, the following snippets will connect to a specified IP address and a specified
machine:
ApdVoIP1.Connect('192.168.12.131');
13
14
ApdVoIP1.Connect('john_work.turbopower.com');
An attempt to create the connection is made immediately. If the connection attempt
succeeds, the OnConnect event is generated. If the connection attempt fails, the OnFail
event is generated.
15
16
17
296 Chapter 9: IP Telephony
1
1
Receiving a VoIP call
1
When receiving a call, the TApdVoIP component initializes the underlying TAPI and H.323
layers and waits for the incoming call. The Connect method of the TApdVoIP component
begins the answering process. When that method is called with a DestAddr of ‘’ (empty
string), the TAPI and H.323 layers are activated and configured to provide status
notifications. When an incoming call is detected, the OnIncomingCall event is generated. If
the Accept parameter of that event is set to True (the default), the call is answered and the
OnConnect event is generated. The following example shows how to receive a VoIP call:
2
3
4
...
ApdVoIP1.Connect('');
...
procedure TForm1.ApdVoIP1IncomingCall(VoIP : TApdCustomVoIP;
CallerAddr: string; var Accept : Boolean);
begin
Accept := MessageDlg('Accept incoming call from ' +
CallerAddr, mtConfirmation, [mbYes, mbNo], 0) = mrOK;
end;
6
7
8
Terminating a VoIP call
Once a call is established through the Connect method of the TApdVoIP component it can
be terminated locally or from the remote station. To terminate the call locally, call the
CancelCall method of the TApdVoIP component. If an active call is present (the OnConnect
event has been generated and the Connected property is True) the OnDisconnect event will
be generated shortly after CancelCall returns. If an active call is not present (after calling
Connect to enter the answer mode, but before the OnConnect event has been generated),
the CancelCall method will not return until the answer mode has
been terminated.
At any point in the call, the remote side could terminate, or the network could become
disconnected. Regardless of the reason, the OnDisconnect event will be generated if the
OnConnect event has been generated previously for this call. If the OnConnect event has
not been generated for this call, the OnFail event will be generated and the Reason
parameter of that event will contain the reason for the failure.
9
10
11
12
13
14
15
16
17
Configuration for VoIP 297
1
1
1
TApdVoIPTerminal Class
2
3
The TApdVoIPTerminal class defines a media terminal. This class is used in the TApdVoIP
component’s AvailableTerminalDevices property. The AvailableTerminalDevices property is
a TStrings type. Each Strings value is the name of a media terminal, each Objects value is a
TApdVoIPTerminal class that defines the capabilities of the given terminal.
4
Hierarchy
TStrings (VCL)
5
6
7
TApdVoIPTerminal (AdVoIP)
Properties
DeviceClass
DeviceName
DeviceInUse
MediaDirection
8
9
10
11
12
13
14
15
16
17
298 Chapter 9: IP Telephony
1
1
MediaType
Reference Section
1
DeviceClass
read-only, run-time property
2
property DeviceClass : TApdTerminalDeviceClass
TApdTerminalDeviceClass = (
dcHandsetTerminal, dcHeadsetTerminal, dcMediaStreamTerminal,
dcMicrophoneTerminal, dcSpeakerphoneTerminal, dcSpeakersTerminal,
dcVideoInputTerminal, dcVideoWindowTerm)
3
4
! Indicates the terminal class of the media terminal.
TAPI 3.x defines several terminal classes. The following table shows which terminal classes
are applicable to which TApdVoIP terminal properties:
TApdVoIP Property
DeviceClass
AudioInDevice
dcHandsetTerminal
6
7
dcHeadsetTerminal
dcMicrophoneTerminal
8
dcSpeakerphoneTerminal
AudioOutDevice
dcHandsetTerminal
9
dcHeadsetTerminal
10
dcSpeakerphoneTerminal
dcSpeakersTerminal
VideoInDevice
11
dcVideoInput
See also: TApdVoIP.AudioInDevice, TApdVoIP.AudioOutDevice, TApdVoIP.VideoInDevice
DeviceInUse
12
read-only, run-time property
13
property DeviceInUse : Boolean
! Indicates whether the terminal is currently being used.
14
This property is True if the terminal is being used by another process, and False if the
terminal is available.
15
16
17
TApdVoIPTerminal Class 299
1
1
1
DeviceName
read-only, run-time property
property DeviceName : string
2
! Indicates the name of the media terminal.
3
This property reflects the name of the media terminal as defined by the media installer. This
property is used to define the Strings value associated with this Object when added to the
AvailableTerminalDevices property.
4
MediaDirection
5
property MediaDirection : TApdMediaDirection
read-only, run-time property
TApdMediaDirection = (mdCapture, mdRender, mdBidirectional);
6
7
8
! Indicates the directional capabilities of the media terminal.
A media terminal can either receive media data, transmit media data, or transmit and
receive media data. The given media terminal’s directional capabilities are indicated by this
property according to the following:
MediaDirection
Description
mdCapture
Can receive (capture) media stream, corresponds
to “In” devices.
mdRender
Can transmit (render) media stream, corresponds
to “Out” devices.
mdBidirectional
Can receive (capture) and transmit (render)
media streams.
9
10
11
MediaType
12
13
14
15
property MediaType : TApdTerminalMediaType
TApdTerminalMediaType = (mtStatic, mtDynamic);
! Indicates how the terminal is instantiated.
A media terminal can be predefined or created as needed. mtStatic terminals usually refer to
hardware devices (speakers, microphones, etc.). mtDynamic terminals are dynamically
created or assigned (video display).
16
17
300 Chapter 9: IP Telephony
1
1
read-only, run-time property
TApdVoIP Component
1
The TApdVoIP component implements Voice over IP (IP Telephony) through the TAPI 3.x
and H.323 interfaces. This component is compatible with Windows 2000, although later
Windows versions will most likely support TAPI 3.x to some extent.
2
3
Hierarchy
4
TOleServer (VCL)
! TApdBaseOleServer (OOMisc) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
TApdCustomVoIP (AdVoIP)
TApdVoIP (AdVoIP)
6
Properties
AudioInDevice
AudioOutDevice
Connected
! Version
AvailableTerminalDevices
VideoInDevice
CallInfo
VideoOutDevice
VoIPAvailable
WaitingForCall
7
8
9
Methods
CancelCall
Connect
ShowMediaSelectDialog
10
11
Events
OnConnect
OnFail
OnDisconnect
OnIncomingCall
12
13
14
15
16
17
TApdVoIP Component 301
1
1
1
Reference Section
AudioInDevice
property
2
property AudioInDevice : string
3
! Determines the media terminal device to use to for audio input.
4
This property is the DeviceName of the TApdVoIPTerminal object that will be used to
provide audio input. Audio input is usually provided through a microphone connected to
the “mic” jack of a sound card.
5
This property supports the “Media options” property editor, which filters media terminals
by type. See “Audio and video device selection” on page 295 for details.
6
See also: AudioOutDevice, AvailableTerminalClasses, VideoInDevice, VideoOutDevice
AudioOutDevice
7
8
9
10
11
property AudioOutDevice : string
! Determines the media terminal device to use for audio output.
This property is the DeviceName of the TApdVoIPTerminal object that will be used to
provide audio output. Audio output is usually provided through speakers connected to the
“speaker” jack of a sound card, although it can also be a headset.
This property supports the “Media options” property editor, which filters media terminals
by type. See “Audio and video device selection” on page 295 for details.
See also: AudioInDevice, AvailableTerminalClasses, VideoInDevice, VideoOutDevice
AvailableTerminalDevices
12
13
14
15
16
property
read-only, run-time property
property AvailableTerminalDevices : TStrings
! Contains references to the available media terminal devices.
When the TApdVoIP component is created, all supported media terminals are enumerated
and their capabilities are retrieved. This information is placed in the
AvailableTerminalDevices property. The Strings value contains the DeviceName of the
terminal, the Objects value contains the TApdVoIPTerminal object.
See the TApdVoIPTerminal description for details on the TApdVoIPTerminal object. See
“Audio and video device selection” on page 295 for details on how
AvailableTerminalDevices can be used.
See also: AudioInDevice, AudioOutDevice, VideoInDevice, VideoOutDevice
17
302 Chapter 9: IP Telephony
1
1
CallInfo
read-only, run-time property
1
property CallInfo : TApdVoIPCallInfo
! A structure containing details about the current call.
2
The TApdVoIP component contains properties for basic call information. TAPI maintains
much more information about the call. Although this information may be of limited use,
CallInfo is provided for projects that may require the extended information.
CallInfo is allocated immediately after the Connect method is called, is updated periodically
by TAPI, and is valid for the duration of the call.
CancelCall
3
4
method
procedure CancelCall;
6
! Terminates the current call.
CancelCall is the universal method for terminating the current call. CancelCall can be used
while waiting for incoming calls, originating a call, negotiating a call and during an
established call. The TApdVoIP component terminates the current TAPI process, releases
any resources allocated for the call, then returns to an idle state.
If an active call is present (the OnConnect event has been generated and the Connected
property is True), the OnDisconnect event will be generated shortly after CancelCall
returns. If an active call is not present (after calling Connect(‘’) to enter the answer mode,
but before the OnConnect event has been generated), the CancelCall method will not return
until the answer mode has been terminated.
See also: Connect, OnConnect, OnDisconnect
7
8
9
10
11
12
13
14
15
16
17
TApdVoIP Component 303
1
1
1
Connect
method
procedure Connect(DestAddr : string);
2
3
4
5
6
7
! Establishes a VoIP connection.
The Connect method is used to establish a Voice over IP call. If DestAddr is an empty string,
Connect waits for an incoming call to be received. If DestAddr is not an empty string,
Connect originates a call to the destination specified by DestAddr.
When originating a call, DestAddr can specify either an IP address or a machine name. See
“Originating a VoIP call” on page 296 for details. The underlying TAPI layer requires an
address-type flag, which indicates whether the address is an IP address or a machine name.
The TApdVoIP component will parse DestAddr to determine the type of address.
When receiving a call, DestAddr must be an empty string. See “Receiving a VoIP call” on
page 297 for details. When receiving a VoIP call, it is not known when that call will be
originated on the remote system. The OnIncomingCall event will be generated when the
incoming call is detected. This event provides the address of the originating system, which
can be used to accept or reject the call.
8
When originating or receiving a call, the OnConnect event is generated when the VoIP call is
connected.
9
See also: CancelCall, Connected, OnConnect, OnIncomingCall, WaitingForCall
Connected
10
11
read-only, run-time property
property Connected
! Indicates whether a connection is currently active or not.
12
This property is set to True when a connection is established (when the OnConnect event is
generated) and False when the connection is terminated (when the OnDisconnect event is
generated). This property can be used to determine whether a call is in progress or not.
13
See also: Connect, OnConnect, OnDisconnect, WaitingForCalls
OnConnect
14
15
event
property OnConnect : TApdVoIPNotifyEvent
TApdVoIPNotifyEvent = procedure (VoIP : TApdCustomVoIP) of object;
! Defines an event handler that is generated when a VoIP connection is established.
16
17
The Connect method begins a background process that can take some time before actually
establishing a connection. The OnConnect event is generated when a VoIP call is actually
connected.
304 Chapter 9: IP Telephony
1
1
When a VoIP call is originated, the OnConnect event is generated after the called station
accepts the call and the H.323 negotiations are complete. When Connect is called to answer
calls, the OnConnect event is generated after the OnIncomingCall event is generated and the
Accept parameter is set to True.
1
2
VoIP is the TApdCustomVoIP component that generated the event.
3
See also: CancelCall, Connect, Connected, OnDisconnect, OnIncomingCall
OnDisconnect
event
4
property OnDisconnect : TApdVoIPNotifyEvent
TApdVoIPNotifyEvent = procedure (VoIP : TApdCustomVoIP) of object;
! Defines an event handler that is generated when a VoIP connection is terminated.
This event handler is generated after calling the CancelCall method to terminate the
connection from the local side and when the connection is terminated from the remote side
or by the network. This event will only be generated if the call has been connected (the
OnConnect event has been generated).
6
7
8
VoIP is the TApdCustomVoIP component that generated the event.
See also: CancelCall, Connect, OnConnect
OnFail
event
10
property OnFail : TApdVoIPFailEvent
TApdVoIPFailEvent = procedure(
VoIP : TApdCustomVoIP; ErrorCode : Integer) of object;
!
9
11
Defines an event handler that is generated when a VoIP call fails.
The Connect method can fail for a variety of reasons, the remote could reject the call, the
network could be congested, the destination address could be invalid, etc. This event
handler is generated when the Connect method fails. This event is also generated when an
established call fails.
12
VoIP is the TApdCustomVoIP component that generated the event. ErrorCode is a non-zero
value indicating the type of failure.
14
See also: Connect, OnDisconnect
15
13
16
17
TApdVoIP Component 305
1
1
1
OnIncomingCall
event
property OnIncomingCall : TApdVoIPIncomingCallEvent
2
3
4
5
6
7
8
9
TApdVoIPIncomingCallEvent = procedure(
VoIP : TApdCustomVoIP; CallerAddr: string; var Accept : Boolean);
! Defines an event handler that is generated when an incoming call is detected.
After calling the Connect method with an empty string parameter, the TApdVoIP
component begins a background process that monitors for incoming calls. When an
incoming call is detected, the OnIncomingCall event is generated. This event provides an
opportunity to accept or reject the call, either based on the CallerAddr or other criteria.
VoIP is the TApdCustomVoIP component that received the incoming call notification and
generated the event. CallerAddr is a form of Caller ID specific to IP Telephony, this string is
usually an IP address or machine name that identifies the system that originated the call.
Accept determines whether the TApdVoIP component accepts or rejects the incoming call.
If Accept is True when this event exits, the call is accepted and the OnConnect event is
generated when the H.323 negotiations are complete. If Accept is False when this event exits,
the call is rejected and the TApdVoIP component will continue monitoring for new
incoming calls. The OnFail and OnDisconnect events will not be generated if the call is not
accepted.
See “Receiving a VoIP call” on page 297 for details and an example.
See also: Connect, OnConnect, OnDisconnect
10
11
ShowMediaSelectDialog
method
function ShowMediaSelectDialog : Boolean;
! Displays a dialog where the audio and video devices can be selected.
12
13
14
The ShowMediaSelectDialog method displays the Media options dialog discussed in the
“Audio and video device selection” on page 295. This dialog box allows the user to select the
media terminals for the AudioInDevice, AudioOutDevice, and VideoInDevice properties.
If OK is clicked, the AudioInDevice, AudioOutDevice and VideoInDevice properties are
updated to reflect the new media terminals and the return value of ShowMediaSelectDialog
is True. If Cancel is clicked, the properties are not changed and this method returns False.
15
The dialog displayed from the ShowMediaSelectDialog method is the same dialog used for
the AudioInDevice, AudioOutDevice and VideoInDevice property editors.
16
See also: AudioInDevice, AudioOutDevice, VideoInDevice
17
306 Chapter 9: IP Telephony
1
1
VideoInDevice
property
1
property VideoInDevice : string
! Determines the media terminal device to use to for video input.
2
This property is the DeviceName of the TApdVoIPTerminal object that will be used to
provide video input. Video input is usually provided through a digital camera or web
camera installed on the system.
This property supports the Media options property editor, which filters media terminals by
type. See “Audio and video device selection” on page 295 for details.
3
4
See also: AudioInDevice, AudioOutDevice, AvailableTerminalClasses, VideoOutDevice
VideoOutDevice
property
6
property VideoOutDevice : TWinControl
! Determines the control where video output is rendered.
7
This property is a TWinControl descendent where the received video stream is displayed. To
display the received video stream, assign the name of a TWinControl descendent, such as a
TPanel, to the VideoOutDevice property.
8
See also: AudioInDevice, AudioOutDevice, AvailableTerminalClasses, VideoInDevice
9
VoIPAvailable
read-only, run-time property
10
property VoIPAvailable : Boolean
! Indicates whether Voice over IP is supported or not.
11
The TApdVoIP component implements Voice over IP (IP Telephony) using TAPI 3.x and the
H.323 TAPI service provider. TAPI 3.x is available for Windows 2000 and later, it is not
available for earlier versions of Windows. When the TApdVoIP component is created, the
TAPI version and H.323 TSP are verified, if these TAPI components are not available the
VoIPAvailable property is set to False; if these TAPI components are available the
VoIPAvailable property is set to True.
12
13
If VoIPAvailable is False, calling the Connect method will raise the EVoIPNotSupported
exception.
14
See also: Connect
15
16
17
TApdVoIP Component 307
1
1
1
WaitingForCall
read-only, run-time property
property WaitingForCall : Boolean
2
! Indicates whether the TApdVoIP component is waiting for incoming calls or not.
3
When the Connect method is called with an empty string, the TApdVoIP component will
begin monitoring for incoming calls. WaitingForCall indicates whether the TApdVoIP
component is waiting for incoming calls or not.
4
See also: Connect, Connected
5
6
7
8
9
10
11
12
13
14
15
16
17
308 Chapter 9: IP Telephony
1
1
Chapter 10: SAPI Components
1
2
The SAPI (Speech API) components in Async Professional provide an easy means to
incorporate speech synthesis and recognition into your programs. The components act as
an interface between the Microsoft SAPI 4 and Delphi and C++ Builder.
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
309
1
1
1
SAPI Overview
2
Async Professional provides speech synthesis, speech recognition and voice telephony
capabilities. These aspects will be explored in further in the following sections.
3
Speech synthesis
4
Speech synthesis will take plain ASCII text and convert it to into digital audio. There are
three primary ways this is done as described in Table 10.1.
5
Table 10.1: Speech synthesis schemes
6
Scheme
Description
Concatenated Word
This technique stitches together recordings of
individual words as provided by the developer
of the speech synthesis engine. This method
can provide high quality output, but is
limited to the words that have been provided
with the engine.
Subword Concatenation
This speech synthesis engine concatenates
short prerecorded phonemes to spell out the
words. The small pieces of audio are smoothed
out to improve the quality of the spoken text.
Synthesis
The speech synthesis engine will simulate the
human vocal chords when it generates the
digital audio. This technique has the
advantage of the ability to adjust the sound
of the voice via a few simple parameters.
7
8
9
10
11
12
13
14
15
16
17
In Async Professional, generic speech synthesis is provided through the TApdSapiEngine
component. The generated speech can either go directly to the sound card or be saved as a
.WAV file for future usage.
While great strides have been made in the quality of computer-synthesized speech, it is still
easily recognizable as computer generated. As of yet, a computer cannot duplicate the
inflection, timing and emotion of human speech.
The speech synthesis engine will do it’s best to pronounce the text given to it. However, some
words and names it will have problems with. The best way to handle this is to provide the
proper phonemes to the speech synthesis engine.
Some speech synthesis engines support input using the International Phonetic Alphabet
(IPA). This is a standardized system of representing phonemes in a Unicode character set. If
your engine supports IPA, this can be used to precisely control the pronunciation of a word.
310 Chapter 10: SAPI Components
1
1
You can verify if an engine supports IPA by checking if tfIPAUnicode is in the features set of
the engine. This can be found in the Features subproperty of the SSVoices property of the
TApdSapiEngine component.
1
2
Speech recognition
Speech recognition converts a spoken audio data stream into ASCII text. In Async
Professional, generic speech recognition is provided through the TApdSapiEngine
component.
Speech recognition requires an object known as a grammar (a list of known words and
possibly how they relate to each other). In Async Professional, the grammar is handled
automatically. Either a list of expected words can be provided in the WordList property, or
the Dictation property can be set to True. In the latter case, the speech synthesis engine will
be provided with a dictation grammar. Most speech recognition engines will then use a
much larger list of known words. Additional words can be specified via the WordList
property.
Using a specific word list is generally faster and more accurate than using the full dictation
grammar.
As words and phrases are recognized the OnPhraseHypothesis and OnPhraseFinish events
will fire to let the application know what words were spoken.
The Microsoft SAPI SDK documentation provides a full list of supported phonemes.
3
4
6
7
8
9
10
Voice telephony
Voice telephony support is provided via the TApdSapiPhone component. This component
allows for speech synthesis and recognition to take place over a voice call. The component is
a descendent of the TApdCustomTAPIDevice and provides all the functionality of a TAPI
based voice call with the capabilities of the TApdCustomSapiEngine.
The voice telephony component has several methods for asking the user for information and
then returning that information in a usable format. For example, the AskForDate method
will prompt the user for a date and then return that date as a TDateTime parameter. Other
methods exist for asking for phone numbers, times, items from a list, extensions and yes or
no replies.
11
12
13
14
15
16
17
SAPI Overview 311
1
1
1
Requirements for speech synthesis and recognition
For speech synthesis and recognition, you will need to have the following:
2
• A 486/33 (DX or SX) or better CPU is the bare minimum required. A Pentium is
recommended.
3
• Speech synthesis will require an additional 1Mb of RAM to that which your program
already needs. Speech recognition will require an additional 8Mb.
4
5
6
7
8
9
10
11
12
• A sound card is needed for both speech synthesis and recognition. For speech
recognition, a microphone is required. Headset microphones are less susceptible to
background noise and are preferable to desktop microphones.
• Microsoft Windows 95 or NT 4.0 or better.
• The Microsoft SAPI controls and a speech recognition and synthesis engine is
needed. A good place to search both for the SAPI software and speech engines is
http://www.microsoft.com/speech/.
• For voice telephony, you will need TAPI, a voice capable modem and speech synthesis
and recognition engines that have been optimized for telephony applications.
Considerations of speech synthesis and recognition
For speech synthesis and recognition to work properly, there are several things that should
be taken into consideration.
For speech recognition, the most significant source of problems will be from the
microphone. The quality of the microphone and the training that the user has done with the
microphone directly affect the ability of the speech recognition engine to interpret what was
said to it. Headset microphones are generally preferable to desktop models.
13
Half-duplex sound cards should be avoided. A sound card is “half-duplex” if it is incapable
of playing and recording audio at the same time. While the card is playing, speech
recognition cannot occur.
14
Feedback between sound card’s output and the microphone can dramatically reduce the
quality of the spoken text.
16
Background noise will also affect the speech recognition. Background conversations, a
radio, door slams or other ambient noise can be picked up by the speech recognition engine
and interpreted in unexpected ways. The use of a higher quality microphone can reduce the
problems, but will not completely eliminate them.
17
Speech recognition is not perfect. Words will be misinterpreted, or not interpreted at all.,
and background noise will be interpreted as words.
15
312 Chapter 10: SAPI Components
1
1
Considerations for voice enabling your application
1
There are numerous things that should be taken into account when developing voiceenabled applications. Here are a few items to keep in mind:
• Speech recognition does make mistakes. If you are using speech recognition to handle
2
commands to your application, you will want confirmation before doing anything
that may be harmful.
3
• Speech synthesis should be used for short phrases. Computer generated voices can
4
quickly annoy your users.
• Speech recognition control of an application as well as speech synthesis should be
optional. Speech recognition should not be used to replace the standard means of
input (that is, the keyboard and mouse).
• In voice telephony, other options should be provided for when speech recognition
fails. DTMF is usually a good backup option.
• Mixing prerecorded text with synthesized speech generally sounds bad. As a rule,
either prerecord everything or nothing.
6
7
8
• Visual feedback should be provided for both speech recognition and synthesis.
Distribution of speech synthesis and speech recognition engines
There are two options for distributing voice-enabled applications. The first is to bundle the
needed speech engines with the application. This will require negotiation with the speech
engine vendor and for the appropriate royalties to be paid.
The second option is to require the user to provide the speech engine. Many sound cards
come bundled with speech engines. The quality of these engines varies dramatically.
9
10
11
12
Async Professional does not come bundled with speech engines.
13
14
15
16
17
SAPI Overview 313
1
1
1
TApdAudioOutDevice Class
2
The TApdAudioOutDevice class is used by the TApdSapiEngine component to encapsulate
the engines used for speech recognition. This class allows for the easy selection of a speech
recognition engine as well as the ability to look at the details of the engines.
3
4
5
6
The properties of this class are laid out as arrays in which each speech recognition engine is
accessed by its index in the array. The CurrentEngine property is used to set the speech
recognition engine. Setting this property to the index of a speech recognition engine will
activate that engine.
Hierarchy
TObject (VCL)
TApdAudioOutDevice (AdSapiEn)
7
Properties
8
9
10
11
12
Age
Features
ModeName
Count
Gender
ProductName
CurrentVoice
Interfaces
Speaker
Dialect
LanguageID
Style
EngineFeatures
MfgName
EngineID
ModeID
Methods
Find
13
14
15
16
17
314 Chapter 10: SAPI Components
1
1
Reference Section
Age
1
read-only, run-time property
2
property Age[x : Integer] : TApdTTSAge
TApdTTSAge = (tsBaby, tsToddler, tsChild,
tsAdolescent, tsAdult, tsElderly, tsUnknown);
3
! Indicates the age of the specified voice.
4
The Age property indicates the age of the voice currently specified . The following table
indicates values for Age:
Value
Approximate Age
tsBaby
1
tsToddler
3
tsChild
6
tsAdolescent
14
tsAdult
20 - 60
tsElderly
Over 60
tsUnknown
Unknown
6
7
8
9
The index of the array indicates the voice that the property refers to. To get the Age property
for the currently active voice, use the CurrentVoice property as the index.
See also: CurrentVoice
Count
10
11
read-only, run-time property
12
property Count : Integer
! Specifies the number of installed speech synthesis voices.
This property indicates the number of speech synthesis voices that are installed. The voices
are numbered from 0 to Count – 1. The properties of the voices are accessed as an array
where the index indicates the voice in question.
The active voice can be activated by setting the CurrentVoice property.
See also: CurrentVoice
13
14
15
16
17
TApdAudioOutDevice Class 315
1
1
1
CurrentVoice
run-time property
property CurrentVoice
2
! Specifies the current speech synthesis voice.
3
This property is used to activate a specific voice for use in speech synthesis. This value
should be set between 0 and Count – 1. This property should not be modified while text is
being spoken.
4
See also: Count, Speak
5
Dialect
read-only, run-time property
property Dialect[x : Integer] : string
6
7
! Indicates the dialect of the specified voice.
Examples of dialects are “Texas” or “New York City.” The actual dialects vary from engine to
engine.
If no specific dialect is used, this property may be either “Standard” or blank.
8
The index of the array indicates the voice that the property refers to. To get the Dialect
property for the currently active voice, use the CurrentVoice property as the index.
9
See also: CurrentVoice
10
EngineFeatures
read-only, run-time property
property EngineFeatures[x : Integer] : Integer
11
! Indicates speech synthesis engine specific features for the specified voice.
12
This property specifies features specific to the speech synthesis engine. The meaning of the
value of this property will depend on the speech synthesis engine.
13
The index of the array indicates the voice that the property refers to. To get the
EngineFeatures property for the currently active voice, use the CurrentVoice property as the
index.
14
See also: CurrentVoice
15
16
17
316 Chapter 10: SAPI Components
1
1
EngineID
read-only, run-time property
1
property EngineID[x : Integer] : string
! Identifies the GUID of the speech synthesis engine.
2
This property identifies the GUID for the speech synthesis engine. This GUID may be used
for multiple voices and languages.
3
The index of the array indicates the voice that the property refers to. To get the EngineID
property for the currently active voice, use the CurrentVoice property as the index.
4
See also: CurrentVoice
Features
read-only, run-time property
property Features[x : Integer] : TApdTTSFeatures
6
TApdTTSFeatures = set of (tfAnyWord, tfVolume, tfSpeed, tfPitch,
tfTagged, tfIPAUnicode, tfVisual, tfWordPosition, tfPCOptimized,
tfPhoneOptimized,tfFixedAudio, tfSingleInstance, tfThreadSafe,
tfIPATextData, tfPreferred, tfTransplanted, tfSAPI4);
7
8
! Identifies what features the speech synthesis engine supports.
The most common speech synthesis engine features are listed in the following table:
9
Feature
Description
tfAnyWord
The speech synthesis engine will attempt to read any
word.
10
tfFixedAudio
The audio device that the speech synthesis engine will
speak to is fixed and cannot be changed.
11
tfIPATextData
The speech synthesis engine supports the IPA
tfIPAUnicode
The speech synthesis engine supports the International
Phonetic Alphabet Unicode character set.
tfPCOptimized
The speech synthesis engine’s voice is optimized to
output to the computer.
13
tfPhoneOptimized
The speech synthesis engine’s voice is optimized for
the telephone. This is required for voice telephony.
14
tfPitch
The speech synthesis engine can adjust the pitch while
it is speaking.
15
tfSAPI4
The engine is designed for SAPI 4.
tfSingleInstance
Only one instance of the speech synthesis engine can
exist at a time.
12
16
17
TApdAudioOutDevice Class 317
1
1
Feature
Description
tfSpeed
The speech synthesis engine can adjust the speaking
rate while it is speaking.
2
tfTagged
The speech synthesis engine can recognize and handle
embedded control tags in the text.
3
tfThreadSafe
The speech synthesis engine is thread safe.
tfTransplanted
The speech synthesis engine supports transplanted
prosody.
tfVisual
The speech synthesis engine provides mouth position
information while it is speaking.
5
tfVolume
The speech synthesis engine can adjust the speaking
volume while it is speaking.
6
tfWordPosition
The speech synthesis engine can send notification of
word position while it is speaking.
1
4
7
The index of the array indicates the voice that the property refers to. To get the Features
property for the currently active voice, use the CurrentVoice property as the index.
8
See also: CurrentVoice
9
Find
method
function Find(Criteria : string) : Integer;
10
! Finds the speech synthesis engine and voice that is the closest match for the input criteria.
11
This method is used to find the best matched speech engine and voice for the requested
features. The format of the Criteria parameter is:
<field>=<value>;<field=value>
12
13
Legal values for field and value are:
Field
Value
Age
Can be one of the following constants: ApdTTSAGE_BABY,
ApdTTSAGE_TODDLER, ApdTTSAGE_CHILD,
ApdTTSAGE_ADOLESCENT, ApdTTSAGE_ADULT,
ApdTTSAGE_ELDERLY. Note: These constants are integers
and will have to be converted to string values for
usages with the Find method.
Dialect
String
EngineFeatures
Integer
EngineId
String
14
15
16
17
318 Chapter 10: SAPI Components
1
1
Field
Value
Features
Can be one of the following constants:
ApdTTSFEATURE_ANYWORD, ApdTTSFEATURE_VOLUME,
ApdTTSFEATURE_SPEED, ApdTTSFEATURE_PITCH,
ApdTTSFEATURE_TAGGED, ApdTTSFEATURE_IPAUNICODE,
ApdTTSFEATURE_VISUAL, ApdTTSFEATURE_WORDPOSITION,
ApdTTSFEATURE_PCOPTIMIZED,
ApdTTSFEATURE_PHONEOPTIMIZED, ApdTTSFEATURE_FIXEDAUDIO,
ApdTTSFEATURE_SINGLEINSTANCE, ApdTTSFEATURE_THREADSAFE,
ApdTTSFEATURE_IPATEXTDATA,
ApdTTSFEATURE_PREFERRED,ApdTTSFEATURE_TRANSPLANTED,
ApdTTSFEATURE_SAPI4. Note: These constants are integers
and will have to be converted to string values for use
with the Find method.
Gender
Can be one of the following constants:
ApdGENDER_NEUTRAL, ApdGENDER_FEMALE, ApdGENDER_MALE.
Note: These constants are integers and will have to be
converted to string values for use with the Find method.
Interfaces
Can be one of the following constants:
ApdTTSI_ILEXPRONOUNCE, ApdTTSI_ITTSATTRIBUTES,
ApdTTSI_ITTSCENTRAL, ApdTTSI_ITTSDIALOGS,
ApdTTSI_ATTRIBUTES, ApdTTSI_IATTRIBUTES,
ApdTTSI_ILEXPRONOUNCE2. Note: These constants are
integers and will have to be converted to string values
for use with the Find method.
LanguageID
Integer
MfgName
String
ModeID
String
ModeName
String
ProductName
String
Speaker
String
Style
String
1
2
3
4
6
7
8
9
10
11
12
13
14
15
16
17
TApdAudioOutDevice Class 319
1
1
1
2
3
4
5
The following code example shows how to use Find:
var
EngineIdx : Integer;
begin
EngineIdx := ApdSapiEngine1.SSVoices.Find(
'Gender=' + IntToStr (ApdGENDER_FEMALE));
EngineIdx := ApdSapiEngine1.SSVoices.Find(
'Gender=' + IntToStr(ApdGENDER_FEMALE) + ';Dialect=Texas');
See also: Count, CurrentEngine
Gender
read-only, run-time property
property Gender[x : Integer] : TApdTTSGender
6
7
8
9
10
11
TApdTTSGender = (tgNeutral, tgFemale, tgMale, tgUnknown);
! Indicates the gender of the specified voice.
The index of the array indicates the voice that the property refers to. To get the Features
property for the currently active voice, use the CurrentVoice property as the index.
See also: CurrentVoice
Interfaces
read-only, run-time property
property Interfaces[x : Integer] : TApdTTSInterfaces
TApdTTSInterfaces = set of (
tiLexPronounce, tiTTSAttributes, tiTTSCentral, tiTTSDialogs,
tiAttributes, tiIAttributes, tiLexPronounce2);
! Indicates the COM interfaces supported by the speech synthesis engine.
12
13
The index of the array indicates the speech synthesis voice that the property refers to. To get
the Interfaces property for the currently active speech synthesis voice, use the CurrentVoice
property as the index.
See also: CurrentVoice
14
15
16
17
320 Chapter 10: SAPI Components
1
1
LanguageID
read-only, run-time property
1
property LanguageID[x : Integer] : Integer
! Specifies the language identifier for the specified voice.
2
Bits 0 through 9 indicate the primary language. Bits 10-15 indicate a sublanguage (or locale).
More information on this can be found in the Windows SDK help under the
MAKELANGID topic.
The index of the array indicates the speech synthesis voice that the property refers to. To get
the LanguageID property for the currently active speech synthesis voice, use the
CurrentVoice property as the index.
3
4
See also: CurrentVoice
MfgName
read-only, run-time property
6
7
property MfgName[x : Integer] : string
! Indicates the name of the manufacturer of the specified voice.
The index of the array indicates the speech synthesis voice that the property refers to. To get
the MfgName property for the currently active speech synthesis voice, use the CurrentVoice
property as the index.
8
9
See also: CurrentVoice
ModeID
read-only, run-time property
property ModeID[x : Integer] : string
!
10
11
Specifies the GUID for the specified voice.
This property indicates the GUID that uniquely identifies each voice installed on the
computer.
12
The index of the array indicates the speech synthesis voice that the property refers to. To get
the ModeID property for the currently active speech synthesis voice, use the CurrentVoice
property as the index.
13
14
See also: CurrentVoice
15
16
17
TApdAudioOutDevice Class 321
1
1
1
ModeName
read-only, run-time property
property ModeName[x : Integer] : string
2
3
! Specifies the name of the specified text to speech mode.
This is the default property of the TApdAudioOutDevice class. Specifying an array index
without any further field specifications will return this property.
4
The index of the array indicates the speech synthesis mode that the property refers to. To get
the ModeName property for the currently active speech synthesis mode, use the
CurrentVoice property as the index.
5
See also: CurrentVoice
ProductName
read-only, run-time property
6
property ProductName[x : Integer] : string
7
! Provides the product name of the specified voice.
8
The index of the array indicates the speech synthesis voice that the property refers to. To get
the ProductName property for the currently active speech synthesis voice, use the
CurrentVoice property as the index.
9
See also: CurrentVoice
Speaker
read-only, run-time property
10
property Speaker[x : Integer] : string
11
12
! Indicates the name of the specified selected speech synthesis voice.
This property specifies the name of the voice of the specified speech synthesis voice. This
property may be blank for some voices.
13
The index of the array indicates the speech synthesis voice that the property refers to. To get
the Speaker property for the currently active speech synthesis voice, use the CurrentVoice
property as the index.
14
See also: CurrentVoice
15
16
17
322 Chapter 10: SAPI Components
1
1
Style
read-only, run-time property
1
property Style[x : Integer] : string
! Indicates the speaking style of the specified voice.
2
Common speaking styles include “Angry”, “Business”, “Calm”, “Casual”, “Computer”,
“Depressed”, “Excited”, “Falsetto”, “Happy”, “Loud”, “Monotone”, “Perky”, “Quiet”,
“Sarcastic”, “Scared”, “Shout”, “Singsong”, “Tense” and “Whisper.”
The index of the array indicates the speech synthesis voice that the property refers to. To get
the Style property for the currently active speech synthesis voice, use the CurrentVoice
property as the index.
3
4
See also: CurrentVoice
6
7
8
9
10
11
12
13
14
15
16
17
TApdAudioOutDevice Class 323
1
1
1
TApdAudioInDevice Class
2
The TApdAudioInDevice class is used by the TApdSapiEngine component to encapsulate
the engines, modes, and voices used by speech synthesis. This class allows for the easy
selection of a speech synthesis engine as well as the ability to look at the details of the speech
synthesis engine.
3
4
5
6
The properties of this are laid out as arrays in which each speech synthesis engine is accessed
by its index in the array. The CurrentVoice property is used to set the speech synthesis
engine. Setting this property to the index of a speech synthesis engine will activate that
engine.
Hierarchy
TObject (VCL)
7
8
9
10
11
TApdAudioInDevice (AdSapiEn)
Properties
Count
Grammars
ModeID
CurrentEngine
Interfaces
ModeName
Dialect
LanguageID
ProductName
EngineFeatures
MaxWordsState
Sequencing
EngineID
MaxWordsVocab
Features
MfgName
12
13
14
15
16
17
324 Chapter 10: SAPI Components
1
1
Reference Section
Count
1
read-only, run-time property
2
property Count : Integer
! Specifies the number of installed speech recognition engines.
3
The engines are numbered from 0 to Count – 1. The properties of the speech recognition
engines are accessed as an array where the index indicates the engine in question.
4
The active speech recognition engine can be activated by setting the CurrentEngine
property.
See also: CurrentEngine
CurrentEngine
run-time property
property CurrentEngine : Integer
!
6
7
Specifies the current speech recognition engine.
This property is used to activate a specific speech recognition engine for use in speech
recognition. This value should be set between 0 and Count – 1.
8
See also: Count, Listen
9
Dialect
read-only, run-time property
10
property Dialect[x : Integer] : string
! Indicates the dialect of the specified language used by the speech recognition engine.
Examples of dialects are “Texas” or “New York City.” The allowed dialects vary from engine
to engine.
11
12
If no specific dialect is used, this property may be either “Standard” or blank.
The index of the array indicates the speech recognition engine that the property refers to. To
get the Dialect property for the currently active speech recognition engine, use the
CurrentEngine property as the index.
13
14
See also: CurrentEngine
15
16
17
TApdAudioInDevice Class 325
1
1
1
EngineFeatures
read-only, run-time property
property EngineFeatures[x : Integer] : Integer
2
! Indicates speech recognition engine specific features for the specified engine.
The meaning of the value of this property will depend on the speech recognition engine.
3
4
The index of the array indicates the speech recognition engine that the property refers to. To
get the EngineFeatures property for the currently active speech recognition engine, use the
CurrentEngine property as the index.
See also: CurrentEngine
5
6
EngineID
read-only, run-time property
property EngineID[x : Integer] : string
! Identifies the GUID of the speech synthesis engine.
7
8
9
This property identifies the GUID for the speech recognition engine. This GUID may be
used for multiple voices and languages.
The index of the array indicates the speech recognition engine to which the property refers.
To get the EngineID property for the currently active speech recognition engine, use the
CurrentEngine property as the index.
See also: CurrentEngine
10
11
12
13
Features
read-only, run-time property
property Features[x : Integer] : TApdSRFeatures
TApdSRFeatures = set of (sfIndepSpeaker, sfIndepMicrophone,
sfTrainWord, sfTrainPhonetic, sfWildcard, sfAnyWord,
sfPCOptimized, sfPhoneOptimized, sfGramList, sfGramLink,
sfMultiLingual, sfGramRecursive, sfIPAUnicode, sfSingleInstance,
sfThreadSafe,sfFixedAudio, sfIPAWord, sfSAPI4);
! Identifies what features the speech recognition engine supports.
14
15
16
17
326 Chapter 10: SAPI Components
1
1
The following table details the meanings of the more common engine features:
1
Feature
Description
sfAnyWord
The speech recognition engine will attempt to
recognize any word.
2
sfFixedAudio
The audio input device is fixed and cannot be
changed.
3
sfGramLink
The speech recognition engine supports automatic
linking between grammars.
sfGramList
The grammars used by the speech recognition engine
support recognition lists.
sfGramRecursive
Context-free grammars support recursive rules.
sfIndepMicrophone
The speech recognition engine is microphone
independent. Retraining is not required if the
microphone changes.
6
sfIndepSpeaker
The speech recognition engine is speaker independent.
It will work well without training.
7
sfIPAUnicode
The engine supports the Unicode International
Phonetic Alphabet.
8
sfMultiLingual
The speech recognition engine is capable of
recognizing multiple languages at a time.
4
9
sfPCOptimized
The speech recognition engine is optimizied for use
on a computer.
sfPhoneOptimized
The speech recognition engine is optimized for use
over a phone. This is required for voice telephony.
sfSAPI4
The engine is designed for SAPI 4.
11
sfSingleInstance
Only a single instance of the voice recognition
engine can be in memory at a time.
12
sfThreadSafe
The speech recognition engine is thread safe.
sfTrainPhonetic
The user can train the speech recognition engine on a
known set of words specifically to train all the
phonemes.
sfTrainWord
The speaker can train the speech recognition engine
on individual words.
sfWildcard
Wildcards are supported in context-free grammars.
10
13
14
15
16
17
TApdAudioInDevice Class 327
1
1
1
The index of the array indicates the speech recognition engine that the property refers to. To
get the Features property for the currently active speech recognition engine, use the
CurrentEngine property as the index.
2
See also: CurrentEngine
3
Grammars
read-only, run-time property
property Grammars[x : Integer] : TApdSRGrammars
4
5
6
7
8
TApdSRGrammars = set of (sgCFG, sgDication, sgLimitedDomain);
! Indicates the types of grammars supported by the speech recognition engine.
They types of grammars supported by the speech recognition engine are listed in the
following table:
Grammar
Description
sgCFG
Context-free grammar
sgDictation
Dictation grammar
sgLimitedDomain
Limited-domain grammar
A speech recognition engine may support multiple grammar types.
9
10
The index of the array indicates the speech recognition engine that the property refers to. To
get the Grammars property for the currently active speech recognition engine, use the
CurrentEngine property as the index.
See also: CurrentEngine
11
12
13
14
15
16
17
328 Chapter 10: SAPI Components
1
1
Interfaces
read-only, run-time property
1
property Interfaces[x : Integer] : TApdSRInterfaces
TApdSRInterfaces = set of (siLexPronounce, siSRAttributes,
siSRCentral, siSRGramCommon, siSRDialogs, siSRGramCFG,
siSRGramDictation, siSRGramInsertionGui, siSREsBasic,
siSREsMerge, siSREsAudio, siSREsCorrection, siSREsEval,
siSREsGraph, siSREsMemory, siSREsModifyGui,
siSREsSpeaker, siSRSpeaker, siSREsScores, siSREsAudioEx,
siSRGramLexPron, siSREsGraphEx, siLexPronounce2,
siAttributes, siSRSpeaker2, siSRDialogs2);
2
3
4
! Indicates the COM interfaces supported by the speech recognition engine.
The index of the array indicates the speech recognition engine that the property refers to. To
get the Interfaces property for the currently active speech recognition engine, use the
CurrentEngine property as the index.
6
See also: CurrentEngine
7
LanguageID
read-only, run-time property
8
property LanguageID[x : Integer] : Integer
! Specifies the language identifier for the specified speech recognition engine.
9
Bits 0 through 9 indicate the primary language. Bits 10-15 indicate a sublanguage (or locale).
More information on this can be found in the Windows SDK help under the
MAKELANGID topic.
The index of the array indicates the speech recognition engine that the property refers to. To
get the LanguageID property for the currently active speech recognition engine, use the
CurrentEngine property as the index.
10
11
12
See also: CurrentEngine
MaxWordsState
read-only, run-time property
13
14
property MaxWordsState[x : Integer] : Integer
! Indicates the maximum number of words in any grammar state for the specified engine.
The index of the array indicates the speech recognition engine that the property refers to. To
get the MaxWordsState property for the currently active speech recognition engine, use the
CurrentEngine property as the index.
15
16
See also: CurrentEngine
17
TApdAudioInDevice Class 329
1
1
1
MaxWordsVocab
read-only, run-time property
property MaxWordsVocab[x : Integer] : Integer
2
! Indicates the maximum number of words in a grammar for the specified speech recognition
engine.
3
4
The index of the array indicates the speech recognition engine that the property refers to. To
get the MaxWordsVocab property for the currently active speech recognition engine, use the
CurrentEngine property as the index.
See also: CurrentEngine
5
6
7
8
9
10
MfgName
property MfgName[x : Integer] : string
! Indicates the name of the manufacturer of the specified speech recognition engine.
The index of the array indicates the speech recognition engine that the property refers to. To
get the MfgName property for the currently active speech recognition engine, use the
CurrentEngine property as the index.
See also: CurrentEngine
ModeID
property ModeID[x : Integer] : string
! Specifies the GUID for the specified speech recognition engine.
This property indicates the GUID that uniquely identifies each speech recognition engine
installed on the computer.
12
The index of the array indicates the speech recognition engine that the property refers to. To
get the ModeID property for the currently active speech recognition engine, use the
CurrentEngine property as the index.
See also: CurrentEngine
14
15
16
17
330 Chapter 10: SAPI Components
1
read-only, run-time property
11
13
1
read-only, run-time property
ModeName
read-only, run-time property
1
property ModeName[x : Integer] : string
! Specifies the name of the specified speech recognition mode.
2
This is the default property of the TApdAudioInDevice class. Specifying an array index
without any further field specifications will return this property.
The index of the array indicates the speech recognition engine that the property refers to. To
get the ModeName property for the currently active speech recognition engine, use the
CurrentEngine property as the index.
3
4
See also: CurrentEngine
ProductName
read-only, run-time property
6
property ProductName[x : Integer] : string
! Provides the product name of the specified speech recognition engine.
7
The index of the array indicates the speech recognition engine that the property refers to. To
get the ProductName property for the currently active speech recognition engine, use the
CurrentEngine property as the index.
8
See also: CurrentEngine
9
Sequencing
read-only, run-time property
10
property Sequencing[x : Integer] : TApdSRSequences
TApdSRSequences = (ssDiscrete, ssContinuous, ssWordSpot,
ssContCFGDiscDict, ssUnknown);
! Indicates the speech recognition scheme.
11
12
13
14
15
16
17
TApdAudioInDevice Class 331
1
1
1
The supported speech recognition schemes are detailed in the following table:
Scheme
Meaning
2
ssContCFGDiscDict
Performs continuous recognition for context-free
grammars. In some cases, this may behave like the
discrete speech recognition scheme.
3
ssContinuous
Continuous recognition. This will recognize words in
a continuous stream of audio with no pauses.
4
ssDiscrete
Each word must be separated by a pause for the speech
recognition engine to recognize it.
ssWordSpot
Searches for known words in a continuous stream of
audio input.
5
6
The index of the array indicates the speech recognition engine that the property refers to. To
get the Sequencing property for the currently active speech recognition engine, use the
CurrentEngine property as the index.
7
See also: CurrentEngine
8
9
10
11
12
13
14
15
16
17
332 Chapter 10: SAPI Components
1
1
TApdCustomSapiEngine Component
1
The TAPdCustomSapiEngine component provides access to the speech synthesis and
recognition engines. The details of the installed engines are accessible via the SREngines and
SSVoices properties.
2
The speech recognition and synthesis engines are automatically initialized at the time of
creation.
3
4
6
7
8
9
10
11
12
13
14
15
16
17
TApdCustomSapiEngine Component 333
1
1
1
Speech Synthesis Tags
2
Some speech synthesis engines support tags that allow for the changing of characteristics of
the spoken text while it is being spoken.
3
All tags begin and end with a backslash. Tags are case insensitive, but white space is
significant. To include a backslash in the text, use a double backslash.
4
An example of tagged text would be:
\Chr=”Business”\This is a \Chr=”monotone”\test.
5
The text “This is a” would be spoken using the Business character and the word test would
be spoken in a monotone.
6
The tags for the speech synthesis engine are defined in Table 10.2.
7
8
Table 10.2: Speech synthesis tags
Tag
Meaning
Chr
Specifies a character for the voice. The
speech synthesis engine defines what values
are allowed for the Chr tag. Common values are
“Angry”, “Business”, “Calm”, “Depressed”,
“Excited”, “Falsetto”, “Happy”, “Loud”,
“Monotone”, “Perky”, “Quiet”, “Sarcastic”,
“Scared”, “Shout”, “Singsong”, “Tense”, and
“Whisper”. The actual values (if any)
supported by the engine will vary from engine
to engine.
Com
Comment. Comments are not spoken. An example
would be \com=“comment”\.
Ctx
Specifies a context for the following text.
The format of the tag is \Ctx=”value”\. Legal
contexts are “Address” for addresses or phone
numbers, “C” for C or C++ code, “Document” for
text documents, “E-Mail” for email, “Numbers”
for numbers, dates and times, “Spreadsheet”
for spreadsheet documentation, “Normal”, for
normal text, and finally “Unknown” for unknown
contexts.
Dem
Demphasizes the next word.
Emp
Emphasizes the next word.
9
10
11
12
13
14
15
16
17
334 Chapter 10: SAPI Components
1
1
Table 10.2: Speech synthesis tags (continued)
1
Tag
Meaning
Enq
Embeds an engine-specific command. The format
is \Eng;GUID;command\ or \Eng;command\ where
GUID is the GUID of the engine and the command
is the command you want to send.
Mrk
Inserts a bookmark in the text. The format of
the tag is \Mrk=number\ where number is the
number of the bookmark.
Pau
Pause for a number of milliseconds. The format
of the tag is \Pau=number\ where number is the
number of milliseconds to pause.
Pit
Sets the baseline pitch in hertz. The format
of the tag is \Pit=number\ where number is the
pitch you want to use.
2
3
4
6
Pra
Sets the pitch range. Format of the tag is
\Pra=value\ where value is the pitch range in
hertz.
7
Prn
Specifies how to pronounce text by passing the
phonetic version to the engine. The format of
the tag is \Prn=text=phonemes\.
8
Pro
Turns on and off prosodic rules. \Pro=1\
activates prosodic rules, \Pro=0\ deactivates
them.
Prt
Indicates the part of speech of the next word.
The format of the tag is \Prt=”type”\ where
type can be “Abbr” for abbreviations, “Adj”
for adjectives, “Adv” for adverbs, “Card” for
cardinal numbers, “Conj” for conjunctions,
“Cont” for contractions, “Det” for
determiners, “Interj” for interjections, “N”
for nouns, “Ord” for ordinal numbers, “Prep”
for prepositions, “Pron” for pronouns, “Prop”
for proper nouns, “Punct” for punctuation,
“Quant” for quantifiers, and “V” for verbs.
RmS
Turns on and off spelling mode. Use \RmS=1\ to
turn on spelling mode and \Rms=0\ to turn it
off.
RmW
Turn on and off pauses between words. Use
\RmW=1\ to insert pauses between words and
\RmW=0\ to turn them off.
9
10
11
12
13
14
15
16
17
Speech Synthesis Tags 335
1
1
1
Table 10.2: Speech synthesis tags (continued)
Tag
Meaning
2
RPit
Sets the relative pitch. The format
\RPit=number\ where number is the pitch. 100
is the default pitch.
3
RPrn
Sets the relative pitch range. The format is
\RPrn=value\ where value is the pitch range.
4
RSpd
Sets the relative speed. The format is
\RSpd=number\ where number is the relative
speed. The default speed is 100.
5
Rst
Resets the speech engine to the default
values.
6
Spd
Sets the average talking speed. The format is
\Spd=number\ where number is speed in words
per minute.
7
Vce
Changes characteristics of the voice. The
format is \Vce=property=value\ where property
can be “Accent”, “Age”, “Dialect” “Gender”,
“Language” or “Style”. The value varies
depending on the property being set.
Vol
Sets the baseline volume for speaking. The
format of the tag is /Vol=number/ where number
is a value between 0 (silence) and 65535
(maximum volume).
8
9
10
11
12
Note: Not all tags are supported by all engines.
Please note that for the ease of reference, the properties, methods, and events of
TApdCustomSapiEngine are documented in the TApdSapiEngine section.
Hierarchy
13
14
TWinControl (VCL)
TApdBaseWinControl (OoMisc). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
TApdCustomSapiEngine (AdSapiEn)
15
16
17
336 Chapter 10: SAPI Components
1
1
TApdSapiEngine Component
1
The TApdSapiEngine component exposes the functionality in the TApdCustomSapiEngine
class, but does not change or add functionality to it. For ease of reference, however, the
properties of TApdSapiEngine are documented here.
2
3
Hierarchy
4
TWinControl (VCL)
! TApdBaseWinControl (OoMisc) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
TApdCustomSapiEngine (AdSapiEn) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 333
6
TApdSapiEngine (AdSapiEn)
Properties
7
CharSet
SRAutoGain
Dictation
SREngines
SRAmplitude
SSVoices
TTSOptions
! Version
8
WordList
9
Methods
Listen
ShowGeneralDlg
SpeakFileToFile
PauseListening
ShowLexiconDlg
SpeakStream
PauseSpeaking
ShowTrainGeneralDlg
SpeakToFile
ResumeListening
ShowTrainMicDlg
StopListening
ResumeSpeaking
Speak
StopSpeaking
ShowAboutDlg
SpeakFile
10
11
12
13
Events
OnInterference
OnSpeakStop
OnSSError
OnPhraseFinish
OnSRError
OnSSWarning
OnPhraseHypothesis
OnSRWarning
OnTrainingRequested
OnSpeakStart
OnSSAttributeChanged
OnVUMeter
14
15
16
17
TApdSapiEngine Component 337
1
1
1
Reference Section
CharSet
property
2
property CharSet : TApdCharacterSet
3
TApdCharacterSet = (csText, csIPAPhonetic, csEnginePhonetic);
! Specifies the character set used in speed synthesis.
4
5
6
csText indicates ASCII text, csIPAPhonetic indicates the International Phonetic Alphabet
and csEnginePhonetic indicates an engine specific phonetic alphabet.
Dictation
property
property Dictation
! Controls whether or not dictation mode is on.
7
8
When dictation mode is on (provided the engine supports it), a larger set of words is used as
the base grammar. When it is off, only the words in the WordList property will be
recognized. Dictation mode may slow down speech recognition.
See Also: WordList
9
10
11
12
Listen
procedure Listen;
! Starts the speech recognition.
Calling Listen will start the speech recognition engine recognizing spoken text. The
OnPhraseHypothesis and OnPhraseFinish events will fire when words are recognized. The
PauseListening, and StopListening events will turn off the speech recognition.
See also: OnPhraseFinish, OnPhraseHypothesis, PauseListening, StopListening
13
14
15
16
17
338 Chapter 10: SAPI Components
1
1
method
OnInterference
event
1
property OnInterference : TApdSRInterferenceEvent
TApdSRInterferenceEvent = procedure(Sender : TObject;
InterferenceType : TApdSRInterferenceType) of object;
2
TApdSRInterferenceType = (itAudioStarted, itAudioStopped,
itDeviceOpened, itDeviceClosed, itNoise, itTooLoud,
itTooQuiet, itUnknown);
3
! Indicates when the audio input to the speech recognition is garbled.
4
This event will fire when the audio input to the speech recognition less than optimal. The
InterferenceType parameter indicates how the input stream was damaged.
The possible values for OnInterference are shown in the following table:
6
Value
Meaning
itAudioStarted
The speech recognition engine has started or resumed
receiving audio data.
7
itAudioStopped
The speech recognition engine has stopped receiving
audio data.
8
itDeviceOpened
The speech recognition engine has opened the input audio
device.
itDeviceClosed
The speech recognition engine has closed the input audio
device.
itNoise
Background noise has interfered with the speech
recognition.
itTooLoud
The user is speaking too loud.
itTooQuiet
The user is speaking to quietly.
itUnknown
The input audio stream was garbled for an unknown
reason.
9
10
11
12
13
14
15
16
17
TApdSapiEngine Component 339
1
1
1
OnPhraseFinish
event
property OnPhraseFinish : TApdSRPhraseFinishEvent
2
3
4
TApdSRPhraseFinishEvent = procedure(
Sender : TObject; const Phrase : WideString) of object;
! Indicates when the user has finished speaking a phrase.
The words the user spoke are in the Phrase parameter of the event. Multiple words may be in
this value.
See also: OnPhraseHypothesis
5
6
7
8
9
OnPhraseHypothesis
event
property OnPhraseHypothesis : TApdSRPhraseHypothesisEvent
TApdSRPhraseHypothesisEvent = procedure(
Sender : TObject; const Phrase : WideString) of object;
! Indicates when the user has finished speaking a phrase, but the speech recognition engine is
not positive on what was spoken.
The best guesss of what the user said will be indicated in the Phrase parameter. Multiple
words may be in this value.
See also: OnPhraseFinish
10
11
OnSpeakStart
event
property OnSpeakStart : TApdSapiNotifyEvent
TApdSapiNotifyEvent = procedure(Sender : TObject) of object;
12
13
14
! Defines an event handler for when speech synthesis starts speaking.
This event will fire when the speech synthesis engine starts speaking to the default audio
device or creates an audio file.
OnSpeakStop
event
property OnSpeakStop : TApdSapiNotifyEvent
15
TApdSapiNotifyEvent = procedure(Sender : TObject) of object;
! Defines an event handler for when the speech synthesis stops speaking.
16
This event will fire when the speech synthesis engine stops speaking to the default audio
device or finishes creating an audio file.
17
340 Chapter 10: SAPI Components
1
1
OnSRError
event
1
property OnSRError : TApdOnSapiError
2
TApdOnSapiError = procedure(
Sender : TObject; Error : LongWord;
const Details : string; const Message : string) of object;
3
! Defines an event handler that is called when a speech recognition error occurs.
An error message was generated at some point during speed recognition. Error indicates the
error code generated by the speech recognition engine. Details provides additional details
about the error and Message provides the error messages as text.
OnSRWarning
4
event
property OnSRWarning : TApdOnSapiError
6
TApdOnSapiError = procedure(
Sender : TObject; Error : LongWord;
const Details : string; const Message : string) of object;
7
! Defines an event handler that is called when a speech recognition warning occurs.
8
A warning message was generated at some point during speed recognition. Error indicates
the error code generated by the speech recognition engine. Details provides additional
details about the warning and Message provides the error messages as text.
OnSSAttributeChanged
event
property OnSSAttributeChanged : TApdSSAttributeChanged
TApdSSAttributeChanged = procedure(
Sender : TObject; Attribute: Integer) of object;
9
10
11
12
! Indicates when an speech synthesis engine attribute has changed.
Multiple applications may be using the same speech synthesis engine. Any one of those can
change attributes. This event exists to let the application know that an attribute has changed.
13
14
15
16
17
TApdSapiEngine Component 341
1
1
1
OnSSError
event
property OnSSError : TApdOnSapiError
2
3
4
TApdOnSapiError = procedure(
Sender : TObject; Error : LongWord; const Details : string;
const Message : string) of object;
! Defines an event handler that is called when a speech synthesis error occurs.
An error message was generated at some point during the speech synthesis. Error indicates
the error code generated by the speech synthesis. Details provides additional details about
the error and Message provides the error messages as text.
5
OnSSWarning
6
property OnSSWarning : TApdOnSapiError
7
TApdOnSapiError = procedure(
Sender : TObject; Error : LongWord; const Details : string;
const Message : string) of object;
8
9
10
11
12
13
14
15
! Defines an event handler that is called when a speech synthesis warning occurs.
A warning message was generated at some point during the speech synthesis. Error indicates
the error code generated by the speech synthesis. Details provides additional details about
the warning and Message provides the error messages as text.
OnTrainingRequested
TApdSRTrainingRequestedEvent = procedure(
Sender : TObject; Training : TApdSRTrainingType) of object;
TApdSRTrainingType = set of (
ttCurrentMic, ttCurrentGrammar, ttGeneral);
! Indicates when the speech recognition engine needs further training.
The OnTrainingRequested event will fire when the speech recognition requests further
training. The Training parameter indicates the type of training required. The
ShowTrainGeneralDlg and ShowTrainMicDlg methods can be called to provide the
necessary training.
See also: ShowTrainGeneralDlg, ShowTrainMicDlg
17
342 Chapter 10: SAPI Components
1
event
property OnTrainingRequested : TApdSRTrainingRequestedEvent
16
1
event
OnVUMeter
event
1
property OnVUMeter : TApdSRVUMeterEvent
2
TApdSRVUMeterEvent = procedure(
Sender : TObject; Level : Integer) of object;
! Indicates the volume of the spoken data.
3
This event provides a rough indicator of the volume of the spoken data heard by the speech
recognition engine. While the speech recognition engine is listening, this event will fire
approximately 8 times a second.
4
See also: Listen, StopListening, SRAmplitude
PauseListening
method
6
procedure PauseListening;
! Pauses speech recognition.
7
PauseListening pauses the speech recognition. ResumeListening must be called to continue
speech recognition. Pauses are nested. For example, if the engine is paused twice, it will take
two resumes to restart the listening.
The speech recognition engine may lose data while it is paused.
8
9
See also: ResumeListening
PauseSpeaking
method
procedure PauseSpeaking;
10
11
! Pauses speaking.
12
This method pauses speech synthesis. To resume speaking, call ResumeSpeaking.
See also: ResumeSpeaking
13
ResumeListening
method
14
procedure ResumeListening;
! Resumes speech recognition.
This method resumes speech recognition after it was paused using PauseListening. Pauses
are nested. For example, if the engine is paused twice, it will take two resumes to restart the
listening.
15
16
See also: PauseListening
17
TApdSapiEngine Component 343
1
1
1
ResumeSpeaking
method
procedure ResumeSpeaking;
2
! Resumes speaking after speaking was paused with PauseSpeaking.
This method will resume speech synthesis after it was paused using PauseSpeaking.
3
See also: PauseSpeaking
4
ShowAboutDlg
method
procedure ShowAboutDlg(const Caption : string);
5
6
! Displays the speech synthesis about dialog box.
The caption parameter provides a caption to the dialog box.
ShowGeneralDlg
7
8
9
10
procedure ShowGeneralDlg(const Caption : string);
! Displays the speech synthesis general settings dialog box.
The speech synthesis general settings dialog allows the user to access various aspects of the
speech synthesis engine. This may include speaker specific information like gender and age,
various engine optimizations, and access to other speech synthesis dialog boxes.
The caption parameter provides a caption to the dialog box.
ShowLexiconDlg
11
12
13
! Displays a dialog box for editing mispronounced words.
This method will display a dialog box that allows the user to edit the pronunciation lexicon
used by speech synthesis.
The caption parameter provides a caption to the dialog box.
15
16
17
344 Chapter 10: SAPI Components
1
method
procedure ShowLexiconDlg(const Caption : string);
14
1
method
ShowTrainGeneralDlg
method
1
procedure ShowTrainGeneralDlg(const Caption : string);
! Displays the general training dialog box.
2
This method will display the general training dialog box. This dialog allows the user to train
the speech recognition engine using a preselected set of words. The speech recognition
engine may request this dialog to be displayed via the OnTrainingRequested event.
3
The caption parameter provides a caption to the dialog box.
4
See also: OnTrainingRequested
ShowTrainMicDlg
method
6
procedure ShowTrainMicDlg(const Caption : string);
! Displays the microphone training dialog box.
This method will display a dialog box in which the user can train the speech recognition
engine for a specific microphone. The speech recognition engine can request for this
training dialog to be displayed via the OnTrainingRequested event.
The Caption parameter allows the application to specify a caption for the title bar of the
dialog.
7
8
9
Note: Not all speech recognition events will support this dialog.
10
See also: OnTrainingRequested
Speak
method
11
procedure Speak(Text : string);
12
! Speaks the specified text to the default audio device.
Calling this method will cause the text specified by the Text parameter to be spoken to the
default audio device.
13
If toTagged is specified in the TTSOptions property, the Text parameter can contain
embedded tags to control the behavior of the speech synthesis engine.
14
The format of the Text parameter is controlled by the CharSet property.
The speech synthesis engine will store the text in a series of buffers. Since each buffer is
spoken independently, complete sentences should be passed to the speech synthesis engine.
15
16
17
TApdSapiEngine Component 345
1
1
1
2
3
4
Normally, the speech synthesis engine will start speaking the text as soon as it passed into
one of the Speak methods (Speak, SpeakFile, SpeakFileToFile, SpeakStream, or
SpeakToFile). However, if PauseSpeaking has been called before one of the Speak methods,
the speaking will not occur until ResumeSpeaking is called.
StopSpeaking can be called at any time to halt the flow of speech. When StopSpeaking is
called, the text that is spoken and any text that is queued will not be spoken. Text that is
queued by using one of the Speak methods after the call to StopSpeaking will be spoken in
the usual fashion.
See also: CharSet, PauseSpeaking, ResumeSpeaking, SpeakFile, SpeakFileToFile,
SpeakStream, SpeakToFile, StopSpeaking, TTSOptions
5
SpeakFile
6
7
8
9
10
11
12
13
14
15
procedure SpeakFile(FileName : string);
! Speaks the contents of the specified file to the default audio device.
Calling this method will cause the contents of the specified file to be read over the default
audio device.
Note: Reading binary files may cause unexpected behavior.
If toTagged is specified in the TTSOptions property, the Text parameter can contain
embedded tags to control the behavior of the speech synthesis engine.
The format of the Text parameter is controlled by the CharSet property.
The speech synthesis engine will store the text in a series of buffers. Since each buffer is
spoken independently, complete sentences should be passed to the speech synthesis engine.
Normally, the speech synthesis engine will start speaking the text as soon as it passed into
one of the Speak methods (Speak, SpeakFile, SpeakFileToFile, SpeakStream, or
SpeakToFile). However, if PauseSpeaking has been called before one of the Speak methods,
the speaking will not occur until ResumeSpeaking is called.
StopSpeaking can be called at any time to halt the flow of speech. When StopSpeaking is
called, the text that is spoken and any text that is queued will not be spoken. Text that is
queued by using one of the Speak methods after the call to StopSpeaking will be spoken in
the usual fashion.
See also: CharSet, PauseSpeaking, ResumeSpeaking, Speak, SpeakFileToFile, SpeakStream,
SpeakToFile, StopSpeaking, TTSOptions
16
17
346 Chapter 10: SAPI Components
1
1
method
SpeakFileToFile
method
1
procedure SpeakFileToFile(const InFile, OutFile : string);
! Converts the contents of the specified file into an audio file.
2
This method will create an audio file named by the OutFile parameter containing the spoken
contents of the InFile parameter. The output file is formatted as xxxxxx.
3
Note: Reading binary files may cause unexpected behavior.
If toTagged is specified in the TTSOptions property, the Text parameter can contain
embedded tags to control the behavior of the speech synthesis engine.
4
The format of the Text parameter is controlled by the CharSet property.
The speech synthesis engine will store the text in a series of buffers. Since each buffer is
spoken independently, complete sentences should be passed to the speech synthesis engine.
Normally, the speech synthesis engine will start speaking the text as soon as it passed into
one of the Speak methods (Speak, SpeakFile, SpeakFileToFile, SpeakStream, or
SpeakToFile). However, if PauseSpeaking has been called before one of the Speak methods,
the speaking will not occur until ResumeSpeaking is called.
StopSpeaking can be called at any time to halt the flow of speech. When StopSpeaking is
called, the text that is spoken and any text that is queued will not be spoken. Text that is
queued by using one of the Speak methods after the call to StopSpeaking will be spoken in
the usual fashion.
See also: CharSet, PauseSpeaking, ResumeSpeaking, Speak, SpeakFile, SpeakStream,
SpeakToFile, StopSpeaking, TTSOptions
6
7
8
9
10
11
SpeakStream
method
procedure SpeakStream(Stream : TStream; FileName : string);
! Speaks the contents of the specified stream.
12
13
This method will speak the contents of the specified stream.
Note: Reading binary files may cause unexpected behavior.
14
If toTagged is specified in the TTSOptions property, the Text parameter can contain
embedded tags to control the behavior of the speech synthesis engine.
15
The format of the Text parameter is controlled by the CharSet property.
The speech synthesis engine will store the text in a series of buffers. Since each buffer is
spoken independently, complete sentences should be passed to the speech synthesis engine.
16
17
TApdSapiEngine Component 347
1
1
1
2
3
4
Normally, the speech synthesis engine will start speaking the text as soon as it passed into
one of the Speak methods (Speak, SpeakFile, SpeakFileToFile, SpeakStream, or
SpeakToFile). However, if PauseSpeaking has been called before one of the Speak methods,
the speaking will not occur until ResumeSpeaking is called.
StopSpeaking can be called at any time to halt the flow of speech. When StopSpeaking is
called, the text that is spoken and any text that is queued will not be spoken. Text that is
queued by using one of the Speak methods after the call to StopSpeaking will be spoken in
the usual fashion.
See also: CharSet, PauseSpeaking, ResumeSpeaking, Speak, SpeakFile, SpeakFileToFile,
SpeakToFile, StopSpeaking, TTSOptions
5
SpeakToFile
6
7
8
9
10
11
12
13
14
15
procedure SpeakToFile (const Text, FileName : string);
! Converts the specified text into an audio file.
Calling this method will cause the text specified by the Text parameter to be converted into
an audio file.
Note: Reading binary files may cause unexpected behavior.
If toTagged is specified in the TTSOptions property, the Text parameter can contain
embedded tags to control the behavior of the speech synthesis engine.
The format of the Text parameter is controlled by the CharSet property.
The speech synthesis engine will store the text in a series of buffers. Since each buffer is
spoken independently, complete sentences should be passed to the speech synthesis engine.
Normally, the speech synthesis engine will start speaking the text as soon as it passed into
one of the Speak methods (Speak, SpeakFile, SpeakFileToFile, SpeakStream, or
SpeakToFile). However, if PauseSpeaking has been called before one of the Speak methods,
the speaking will not occur until ResumeSpeaking is called.
StopSpeaking can be called at any time to halt the flow of speech. When StopSpeaking is
called, the text that is spoken and any text that is queued will not be spoken. Text that is
queued by using one of the Speak methods after the call to StopSpeaking will be spoken in
the usual fashion.
See also: CharSet, PauseSpeaking, ResumeSpeaking, Speak, SpeakFile, SpeakFileToFile,
SpeakStream, StopSpeaking, TTSOptions
16
17
348 Chapter 10: SAPI Components
1
1
method
SREngines
run-time property
1
property SREngines : TApdAudioInDevice
! Lists the available text to speech engines and specifies the current engine.
SSVoices
2
run-time property
3
property SSVoices : TApdAudioOutDevice
! Lists the available speech recognition engines and specifies the current engine.
StopListening
4
method
procedure StopListening;
! Stops speech recognition.
6
This method will halt the speech recognition engine. If you call this method before calling
Listen, an error will be generated.
7
See also: Listen, PauseListening, ResumeListening
SRAmplitude
read-only, run-time property
8
9
property SRAmplitude : Word
! Provides the loudness of words spoken to the speech recognition engine.
Provides the current volume of the audio data spoken to the speech recognition engine. This
value is the same as the Level parameter passed into the OnVUMeter event.
10
11
See also: Listen, OnVUMeter
SRAutoGain
property
12
property SRAutoGain : Integer
! Controls the automatic gain settings of the speech recognition engine.
This property controls the degree to which the speech recognition engine can automatically
adjust the gain. Legal values range from 0, in which the speech recognition engine cannot
adjust the gain, to 100, in which the gain is entirely controlled by the speech recognition
engine.
Values between 0 and 100 set the percentage of gain adjustment that the speech engine can
make. For example, a SRAutoGain value of 50 would cause the speech recognition engine to
only adjust the gain half as much as it normally would.
13
14
15
16
17
TApdSapiEngine Component 349
1
1
1
StopSpeaking
method
procedure StopSpeaking;
2
3
! Stops speaking.
StopSpeaking halts the flow of speech being generated by the speech synthesis engine. This
will also cancel any queued speech requests.
See also: Speak, PauseSpeaking, ResumeSpeaking
4
5
TTSOptions
property
property TTSOptions : TApdTTSOptions
TApdTTSOptions = set of (toTagged);
6
7
8
! Specifies options used during speech synthesis.
This property sets the speech synthesis options that will be used when text is spoken. The
toTagged value allows the text to be spoken to contain embedded tags.
WordList
property
property WordList : TStringList
9
10
11
! Specifies expected words to be used by the speech recognition engine.
This property specifies those words that the speech recognition will be listening for. If
Dictation is False, only the words in this property will the speech engine listen for.
Otherwise, the speech engine will listen for the specified words in addition to those words in
the default dictation word list. When Dictation is True, this property is optional.
See Also: Dictation
12
13
14
15
16
17
350 Chapter 10: SAPI Components
1
1
TApdSapiPhonePrompts
1
The TApdSapiPhonePrompts class stores default prompts for use by the
TApdCustomSapiPhone component. These values are spoken to the users when
information is requested by one of the AskFor methods in the TApdCustomSapiPhone
component.
2
Hierarchy
4
3
TPersistent (VCL)
TApdSapiPhonePrompts (AsSapiPh)
6
Properties
AskAreaCode
Help2
TooManyDigits
AskLastFour
Main
Unrecognized
AskNextThree
Main2
Where
Help
TooFewDigits
Where2
7
8
9
10
11
12
13
14
15
16
17
TApdSapiPhonePrompts 351
1
1
1
Reference Section
AskAreaCode
property
2
property AskAreaCode : string
3
4
5
6
! Prompt for user to enter an area code.
AskAreaCode specifies the spoken prompt used to ask the user for their area code.
AskForPhoneNumber and AskForPhoneNumberEx use this when the speech recognition
engine is unable to understand the full phone number. If the speech recognition engine fails
to recognize the full phone number, the user will first be prompted for their area code using
the AskAreaCode property, followed by the middle three digits of the phone number using
the AskNextThree property and finally the last four digits using the prompt in the
AskLastFour property.
See also: AskLastFour, AskNextThree
7
8
AskLastFour
property
property AskLastFour : string
! Prompt for user to enter the last four digits of the phone number.
9
10
11
12
AskLastFour specifies the spoken prompt used to ask the user for the last four digits of their
phone number. AskForPhoneNumber and AskForPhoneNumberEx use this when the
speech recognition engine is unable to understand the full phone number. If the speech
recognition engine fails to recognize the full phone number, the user will first be prompted
for their area code using the AskAreaCode property, followed by the middle three digits of
the phone number using the AskNextThree property and finally the last four digits using the
prompt in the AskLastFour property.
See also: AskAreaCode, AskNextThree
13
14
15
16
17
352 Chapter 10: SAPI Components
1
1
AskNextThree
property
1
property AskNextThree : string
! Prompt for user to enter the middle three digits of the phone number.
2
AskNextThree specifies the spoken prompt used to ask the user for middle three digits of
their phone number. AskForPhoneNumber and AskForPhoneNumberEx use this when the
speech recognition engine is unable to understand the full phone number. If the speech
recognition engine fails to recognize the full phone number, the user will first be prompted
for an area code using the AskAreaCode property, followed by the middle three digits of the
phone number using the AskNextThree property and finally the last four digits using the
prompt in the AskLastFour property.
3
4
See also: AskAreaCode, AskLastFour
Help
property
6
7
property Help : string
! Specifies help text that will be spoken if the user asks for help.
If the user asks for help during a call, first the text in the Help property will be spoken. If the
user asks for help a second time, the text in Help2 will be spoken.
8
See also: Help2
9
Help2
property
10
property Help2 : string
! Specifies help text that will be spoken if the user asks for help a second time.
If the user asks for help during a call, first the text in the Help property will be spoken. If the
user asks for help a second time, the text in Help2 will be spoken.
See also: Help
11
12
13
14
15
16
17
TApdSapiPhonePrompts 353
1
1
1
Main
property
property Main : string
2
3
4
! Specifies the main prompt spoken to the user.
Main is the first prompt spoken to the user by the AskFor methods in the
TApdCustomSapiPhone component. The contents of this property should reflect what the
user is being asked for. If the main prompt is repeated, the value in Main2 will be spoken
instead of Main.
See also: Main2
5
6
7
8
9
10
11
Main2
property Main2 : string
! Specifies the main prompt spoken to the user if the main prompt needs to be repeated.
Main2 is alternative and generally shorter text spoken if the main prompt should need to be
repeated. The contents of this property should reflect what the user is being asked for. If the
main prompt is repeated, the value in Main2 will be spoken instead of Main.
See also: Main
TooFewDigits
! Prompt spoken to the user if too few digits were specified for an extension.
See also: TooManyDigits
TooManyDigits
property TooManyDigits : string
! Prompt spoken to the user if too many digits were specified for an extension.
See also: TooFewDigits
14
15
16
17
354 Chapter 10: SAPI Components
1
1
property
property TooFewDigits : string
12
13
property
property
Unrecognized
property
1
property Unrecognized : string
! Prompt spoken to the user to indicate the speech recognition engine did not understand
2
what they said.
Where
property
property Where : string
3
4
! Help text spoken to the user if they ask where they are.
The Where prompt is spoken in response when the user asks, “Where Am I?” If the user asks
where they are again, the text in Where2 will be spoken.
See also: Where2
Where2
6
property
7
property Where2 : string
! Help text spoken to the user if they ask where they are a second time.
8
If the user asks where they are again for a second time, the text in Where2 will be spoken.
9
See also: Where
10
11
12
13
14
15
16
17
TApdSapiPhonePrompts 355
1
1
1
TApdCustomSapiPhone Component
2
The TApdCustomSapiPhone component is a speech-aware TAPI device. This component
will automatically link to a TApdCustomSapiEngine component. When a call is active,
speech synthesis and recognition will take place over the voice TAPI connection.
3
4
For this to work you will need a voice capable modem and speech synthesis and recognition
engines that have been optimized for telephone usage.
5
Note: Speech recognition may lag behind what is actually spoken. Setting the Dictation
property of the TApdCustomSapiEngine component to False helps with this, but the
operating system, modem type, amount of memory and other factors influence the speech
recognition lag.
6
" Caution: When dialing a voice call, the standard TAPI Service Provider will return that it is
7
8
9
10
11
12
13
14
connected as soon as it has dialed. This is a known problem in the standard TAPI Service
Provider provided with Windows.
Most SAPI calls consist of a series of questions for the user. These are handled by several
methods: AskForDate, AskForTime, AskForPhoneNumber and related methods. These
methods will prompt the user for some information and then collect the voice reply and
interpret the reply into a usable value.
The return code for the AskFor methods lets you know what the state of the call is. The reply
codes are detailed in Table 10.3. Several of these values are controlled by the Options
property.
Table 10.3: AskFor methods return codes
Return code
Description
prOk
The method was able to get a reply from the
user.
prAbort
An unrecoverable error occurred in the method.
prNoResponse
The user did not respond.
prOperator
The user asked to speak to an operator. This
can be disabled in the Options property.
prHangUp
The user either asked to hang up or did hang
up. Asking to hang up can be disabled in the
Options property.
prBack
The user asked to go back. This can be
disabled in the Options property.
15
16
17
356 Chapter 10: SAPI Components
1
1
Table 10.3: AskFor methods return codes (continued)
1
Return code
Description
prWhere
The user asked where he or she is. This is not
normally returned.
2
prHelp
The user asked for help. This is not normally
returned.
3
prRepeat
The user asked for the current prompt to be
repeated. This is not normally returned.
prSpeakFaster
The user asked for the speech synthesis engine
to speak faster. This is not normally
returned. This can be disabled in the Options
property.
prSpeakSlower
The user asked for the speech synthesis engine
to speak slower. This is not normally
returned. This can be disabled in the Options
property.
prCheck
A reply was returned from the user, but the
method had difficulty in interpreting it. The
returned value may be unreliable. This most
applies to asking for dates and times. For
these methods, the textual data is returned to
aid in interpreting the value.
prError
The reply from user was entirely
unintelligible.
prUnknown
An unexpected value was returned.
4
6
7
8
9
10
11
Full and half duplex telephony connections
Most voice modems are half duplex—that is, they cannot listen and talk at the same time. In
addition, the TAPI Service Provider may also be half duplex. UnimodemV, even under
Windows 2000, has half duplex wave drivers. Even if you do have a full duplex modem, the
wave drivers will prevent you from operating in full duplex mode.
Please note that for the ease of reference, the properties methods and events of
TApdCustomSapiPhone are documented in the TApdSapiPhone section.
12
13
14
15
16
17
TApdCustomSapiPhone Component 357
1
1
1
Hierarchy
TComponent
2
3
TApdBaseComponent (OoMisc). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
TApdCustomTapiDevice (AdTapi)
TApdCustomSapiPhone (AdSapiPh)
4
5
6
7
8
9
10
11
12
13
14
15
16
17
358 Chapter 10: SAPI Components
1
1
TApdSapiPhone Component
1
The TApdSapiPhone component exposes the functionality in the TApdCustomSapiPhone
class, but does not change or add functionality to it. For ease of reference, however, the
properties of TApdSapiEngine are documented here.
2
3
Hierarchy
4
TComponent
! TApdBaseComponent (OoMisc) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
TApdCustomTapiDevice (AdTapi)
6
TApdCustomSapiPhone (AdSapiPh)
TApdSapiPhone (AdSapiPh)
7
Properties
NoAnswerMax
Options
NoAnswerTime
Prompts
NumDigits
SapiEngine
! Version
8
9
Methods
10
AskForDate
AskForListEx
AskForTime
AskForDateEx
AskForPhoneNumber
AskForTimeEx
AskForExtension
AskForPhoneNumberEx
AskForYesNo
AskForExtensionEx
AskForSpelling
AskForYesNoEx
AskForList
AskForSpellngEx
Speak
11
12
13
14
15
16
17
TApdSapiPhone Component 359
1
1
1
Reference Section
AskForDate
method
2
3
4
5
6
7
8
function AskForDate(
var OutDate : TDateTime; var ParsedText : string;
NewPrompt1 : string) : TApdSapiPhoneReply;
TApdSapiPhoneReply = (prOk, prAbort, prNoResponse,
prOperator, prHangUp, prBack, prWhere, prHelp, prRepeat,
prSpeakFaster, prSpeakSlower, prCheck, prError, prUnknown);
! Asks the user for a date.
The user will be prompted for the date with the value in NewPrompt1. If this value is blank,
then the default main prompt in the Prompts property will be used. Other prompts used in
asking for the date are obtained from the Prompts property. If you want to override the
prompts other than the main prompt, use the AskForDateEx method.
The date is returned in OutDate as a TDateTime. The date returned may be unreliable. If this
is the case, the method will return prCheck and the parsed text spoken by the user will be
returned in ParsedText.
See also: AskForDateEx, Prompts
9
AskForDateEx
10
11
12
13
14
function AskForDateEx(var OutDate : TDateTime;
var ParsedText : string; NewPrompt1 : string;
NewPrompt2 : string; NewHelp1 : string;
NewHelp2 : string; NewWhere1 : string;
NewWhere2 : string) : TApdSapiPhoneReply;
TApdSapiPhoneReply = (prOk, prAbort, prNoResponse,
prOperator, prHangUp, prBack, prWhere, prHelp, prRepeat,
prSpeakFaster, prSpeakSlower, prCheck, prError,
prUnknown);
! Asks the user for a date.
15
16
17
360 Chapter 10: SAPI Components
1
1
method
The user will be prompted for the date using the prompts passed into the method. These
prompts correspond to the values in the Prompts property. If a value is blank, then the value
from the corresponding prompt in the Prompts property will be used. If you only need to
override the main prompt, use the AskForDate method.
1
2
The date is returned in OutDate as a TDateTime. The date returned may be unreliable. If this
is the case, the method will return prCheck and the parsed text spoken by the user will be
returned in ParsedText.
3
See also: AskForDate, Prompts
4
AskForExtension
method
function AskForExtension(var Extension : string;
NewPrompt1 : string) : TApdSapiPhoneReply;
TApdSapiPhoneReply = (prOk, prAbort, prNoResponse,
prOperator, prHangUp, prBack, prWhere, prHelp, prRepeat,
prSpeakFaster, prSpeakSlower, prCheck, prError,
prUnknown);
! Asks the user for a telephone extension.
6
7
8
The user will be prompted for the telephone extension with the value in NewPrompt1. If this
value is blank, then the default main prompt in the Prompts property will be used. Other
prompts used in asking for the extension are obtained from the Prompts property. If you
want to override the prompts other than the main prompt, use the AskForExtensionEx
method.
10
The extension is assumed to be a series of digits and is returned as a string in the Extension
parameter.
11
9
See also: AskForExtensionEx, Prompts
12
13
14
15
16
17
TApdSapiPhone Component 361
1
1
1
2
3
4
5
AskForExtensionEx
Method
function AskForExtensionEx(var Extension : string;
NewPrompt1 : string; NewPrompt2 : string;
NewTooManyDigits : string; NewTooFewDigits : string;
NewNumDigits : Integer; NewHelp1 : string;
NewHelp2 : string; NewWhere1 : string;
NewWhere2 : string) : TApdSapiPhoneReply;
TApdSapiPhoneReply = (prOk, prAbort, prNoResponse,
prOperator, prHangUp, prBack, prWhere, prHelp, prRepeat,
prSpeakFaster, prSpeakSlower, prCheck, prError,
prUnknown);
! Asks the user for a telephone extension.
6
7
8
9
The user will be prompted for the telephone extension using the prompts passed into the
method. These prompts correspond to the values in the Prompts property. If a value is
blank, then the value from the corresponding prompt in the Prompts property will be used.
If you only need to override the main prompt, use the AskForExension method.
The extension is assumed to be a series of digits and is returned as a string in the Extension
parameter.
See also: AskForExtension, Prompts
AskForList
10
11
12
13
14
15
16
function AskForList(List : TStringList; var OutIndex : Integer;
NewPrompt1 : string) : TApdSapiPhoneReply;
TApdSapiPhoneReply = (prOk, prAbort, prNoResponse,
prOperator, prHangUp, prBack, prWhere, prHelp, prRepeat,
prSpeakFaster, prSpeakSlower, prCheck, prError,
prUnknown);
! Asks the user for a value in a list.
The user will be prompted for an entry in a list with the value in NewPrompt1. If this value is
blank, then the default main prompt in the Prompts property will be used. Other prompts
used in asking for the value are obtained from the Prompts property. If you want to override
the prompts other than the main prompt, use the AskForListEx method.
The list of items the user is asked to select an item from is provided in the List parameter. If
the user replies with one of the items in the list, that value will be retuned in the OutIndex
parameter. If no item is selected, OutIndex will be set to –1.
See also: AskForListEx, Prompts
17
362 Chapter 10: SAPI Components
1
1
method
AskForListEx
method
1
function AskForListEx(List : TStringList;
var OutIndex : Integer; NewPrompt1 : string;
NewPrompt2 : string; NewHelp1 : string;
NewHelp2 : string; NewWhere1 : string;
NewWhere2 : string) : TApdSapiPhoneReply;
2
TApdSapiPhoneReply = (prOk, prAbort, prNoResponse,
prOperator, prHangUp, prBack, prWhere, prHelp, prRepeat,
prSpeakFaster, prSpeakSlower, prCheck, prError,
prUnknown);
4
3
! Asks the user for a value in a list.
The user will be prompted for the value in a list using the prompts passed into the method.
These prompts correspond to the values in the Prompts property. If a value is blank, then the
value from the corresponding prompt in the Prompts property will be used. If you only need
to override the main prompt, use the AskForList method.
The list of items the user is asked to select an item from is provided in the List parameter. If
the user replies with one of the items in the list, that value will be retuned in the OutIndex
parameter. If no item is selected, OutIndex will be set to –1.
See also: AskForList, Prompts
6
7
8
9
AskForPhoneNumber
method
function AskForPhoneNumber(var PhoneNumber : string;
NewPrompt1 : string) : TApdSapiPhoneReply;
TApdSapiPhoneReply = (prOk, prAbort, prNoResponse,
prOperator, prHangUp, prBack, prWhere, prHelp, prRepeat,
prSpeakFaster, prSpeakSlower, prCheck, prError,
prUnknown);
! Asks the user for a phone number.
10
11
12
13
The user will be prompted for the phone number with the value in NewPrompt1. If this
value is blank, then the default main prompt in the Prompts property will be used. Other
prompts used in asking for the phone number are obtained from the Prompts property. If
you want to override the prompts other than the main prompt, use the
AskForPhoneNumberEx method.
14
15
16
17
TApdSapiPhone Component 363
1
1
1
2
3
4
5
6
7
8
9
10
11
If AskForPhoneNumber is unable to obtain the phone number, it will ask for the number in
pieces using the AskAreaCode, AskNextThree and AskLastFour prompts.
The phone number is returned as a string in the PhoneNumber property.
See also: AskForPhoneNumberEx, Prompts
AskForPhoneNumberEx
function AskForPhoneNumberEx(var PhoneNumber : string;
NewPrompt1 : string; NewPrompt2 : string;
NewAskAreaCode : string; NewAskNextThree : string;
NewAskLastFour : string; NewHelp1 : string;
NewHelp2 : string; NewWhere1 : string;
NewWhere2 : string) : TApdSapiPhoneReply;
TApdSapiPhoneReply = (prOk, prAbort, prNoResponse,
prOperator, prHangUp, prBack, prWhere, prHelp, prRepeat,
prSpeakFaster, prSpeakSlower, prCheck, prError,
prUnknown);
! Asks the user for a phone number.
The user will be prompted for the phone number using the prompts passed into the method.
These prompts correspond to the values in the Prompts property. If a value is blank, then the
value from the corresponding prompt in the Prompts property will be used. If you only need
to override the main prompt, use the AskForPhoneNumber method.
If AskForPhoneNumber is unable to obtain the phone number, it will ask for the number in
pieces using the NewAskAreaCode, NewAskNextThree and NewAskLastFour parameters
or the corresponding values in Prompts.
The phone number is returned as a string in the PhoneNumber property.
12
See also: AskForPhoneNumber, Prompts
13
14
15
16
17
364 Chapter 10: SAPI Components
1
1
method
AskForSpelling
method
function AskForSpelling(
var SpelledWord : string;
NewPrompt1 : string) : TApdSapiPhoneReply;
2
TApdSapiPhoneReply = (prOk, prAbort, prNoResponse,
prOperator, prHangUp, prBack, prWhere, prHelp, prRepeat,
prSpeakFaster, prSpeakSlower, prCheck, prError,
prUnknown);
3
4
! Asks the user for a spelled word.
The user will be prompted using the value in NewPrompt1 to spell out a reply. If
NewPrompt1 is blank, then the default main prompt in the Prompts property will be used.
Other prompts used in asking for the spelled item are obtained from the Prompts property.
If you want to override the prompts other than the main prompt, use the AskForSpellingEx
method.
6
7
The reply from the user is returned in the SpelledWord parameter.
See also: AskForSpellingEx, Prompts
8
AskForSpellingEx
method
function AskForSpellingEx(
var SpelledWord : string; NewPrompt1 : string;
NewPrompt2 : string; NewHelp1 : string; NewHelp2 : string;
NewWhere1 : string; NewWhere2 : string) : TApdSapiPhoneReply;
TApdSapiPhoneReply = (prOk, prAbort, prNoResponse,
prOperator, prHangUp, prBack, prWhere, prHelp, prRepeat,
prSpeakFaster, prSpeakSlower, prCheck, prError,
prUnknown);
!
1
9
10
11
12
Asks the user for a spelled word.
The user will be prompted using the prompts passed into the method to spell out a reply.
The prompts correspond to the values in the Prompts property. If a value is blank, then the
value from the corresponding prompt in the Prompts property will be used. If you only need
to override the main prompt, use the AskForSpelling method.
13
The reply from the user is returned in the SpelledWord parameter.
15
14
See also: AskForSpelling, Prompts
16
17
TApdSapiPhone Component 365
1
1
1
2
3
4
5
6
AskForTime
function AskForTime(
var OutTime : TDateTime; var ParsedText : string;
NewPrompt1 : string) : TApdSapiPhoneReply;
TApdSapiPhoneReply = (prOk, prAbort, prNoResponse,
prOperator, prHangUp, prBack, prWhere, prHelp, prRepeat,
prSpeakFaster, prSpeakSlower, prCheck, prError,
prUnknown);
! Asks the user for a time.
The user will be prompted for the time with the value in NewPrompt1. If this value is blank,
then the default main prompt in the Prompts property will be used. Other prompts used in
asking for the time are obtained from the Prompts property. If you want to override the
prompts other than the main prompt, use the AskForTimeEx method.
7
The date is returned in OutTime as a TDateTime. The date returned may be unreliable. If
this is the case, the method will return prCheck and the parsed text spoken by the user will
be returned in ParsedText.
8
See also: AskForTimeEx, Prompts
9
10
11
12
13
14
15
AskForTimeEx
1
method
function AskForTimeEx(
var OutTime : TDateTime; var ParsedText : string;
NewPrompt1 : string; NewPrompt2 : string; NewHelp1 : string;
NewHelp2 : string; NewWhere1 : string;
NewWhere2 : string) : TApdSapiPhoneReply;
TApdSapiPhoneReply = (prOk, prAbort, prNoResponse,
prOperator, prHangUp, prBack, prWhere, prHelp, prRepeat,
prSpeakFaster, prSpeakSlower, prCheck, prError,
prUnknown);
! Asks the user for a time.
The user will be prompted for the time using the prompts passed into the method. These
prompts correspond to the values in the Prompts property. If a value is blank, then the value
from the corresponding prompt in the Prompts property will be used. If you only need to
override the main prompt, use the AskForTime method.
16
The date is returned in OutTime as a TDateTime. The date returned may be unreliable. If
this is the case, the method will return prCheck and the parsed text spoken by the user will
be returned in ParsedText.
17
See also: AskForTime, Prompts
366 Chapter 10: SAPI Components
1
method
AskForYesNo
method
function AskForYesNo(
var Reply : Boolean; NewPrompt1 : string) : TApdSapiPhoneReply;
TApdSapiPhoneReply = (prOk, prAbort, prNoResponse,
prOperator, prHangUp, prBack, prWhere, prHelp, prRepeat,
prSpeakFaster, prSpeakSlower, prCheck, prError,
prUnknown);
2
3
! Asks the user for a yes or no reply.
4
The user will be prompted for a yes or no reply using the value in NewPrompt1. If this value
is blank, then the default main prompt in the Prompts property will be used. Other prompts
used in asking for the yes or no reply are obtained from the Prompts property. If you want to
override the prompts other than the main prompt, use the AskForYesNoEx method.
The date is returned in the Reply parameter as a Boolean. A value of True indicates yes and a
value of False indicates no.
See also: AskForYesNoEx, Prompts
AskForYesNoEx
method
function AskForYesNoEx(
var Reply : Boolean; NewPrompt1 : string;
NewPrompt2 : string; NewHelp1 : string;
NewHelp2 : string; NewWhere1 : string;
NewWhere2 : string) : TApdSapiPhoneReply;
6
7
8
9
10
TApdSapiPhoneReply = (prOk, prAbort, prNoResponse,
prOperator, prHangUp, prBack, prWhere, prHelp, prRepeat,
prSpeakFaster, prSpeakSlower, prCheck, prError,
prUnknown);
!
1
11
12
Asks the user for a yes or no reply.
The user will be prompted for the reply using the prompts passed into the method. These
prompts correspond to the values in the Prompts property. If a value is blank, then the value
from the corresponding prompt in the Prompts property will be used. If you only need to
override the main prompt, use the AskForYesNo method.
13
The date is returned in the Reply parameter as a Boolean. A value of True indicates yes and a
value of False indicates no.
15
See also: AskForYesNo, Prompts
16
14
17
TApdSapiPhone Component 367
1
1
1
NoAnswerMax
property
property NoAnswerMax : Integer
2
3
! Specifies the number of times the user can not answer a prompt before returning an error.
This is used by the AskFor methods. If there is no answer, these methods will return
prNoResponse.
See also: NoAnswerTime
4
5
NoAnswerTime
property
property NoAnswerTime : Integer
! Specifies in seconds the amount of time for a user to answer a prompt.
6
This is used by the AskFor methods. If the user does not reply in NoAnswerTime seconds
NoAnswerMax times, the prNoResponse error will be returned by the method.
7
See also: NoAnswerMax
8
NumDigits
property
property NumDigits : Integer
9
! Specifies the number of digits in the extension asked for by AskForExtension and
AskForExtensionEx.
10
Options
11
property Options : TApdSapiPhoneSettings
12
TApdSapiPhoneSettings = set of (psVerify, psCanGoBack,
psDisableSpeedChange, psEnableOperator,
psEnableAskHangup, psFullDuplex);
13
! Specifies options supported by the call.
14
15
16
17
368 Chapter 10: SAPI Components
1
1
property
Various options can be enabled for a SAPI call. These are detailed in the following table:
1
Option
Meaning
psVerify
Every AksFor method will confirm the user’s reply
automatically.
2
psCanGoBack
The user can ask to go back. This enables the
prBack return value in the AskFor methods.
3
psDisableSpeedChange
If selected, the user cannot ask for the speech
synthesis to speak faster or slower.
psEnableOperator
The user can ask for an operator. This enables the
prOperator return value in the AskFor methods.
psEnableAskHangup
The user can ask to hang up. This enables the
prHangUp return value in the AskFor methods. This
will not automatically hang up.
psFullDuplex
Enables the telephony control to listen at the same
time it is speaking. In most cases, this cannot be
turned on. Full duplex depends on the modem drivers
in addition to the TAPI service provider.
Prompts
property
property Prompts : TApdSapiPhonePrompts
4
6
7
8
9
! Specifies the default spoken prompts used in getting information from the user.
SapiEngine
property
property SapiEngine : TApdCustomSapiEngine
10
11
! Specifies the TApdCustomSapiEngine component to use for speech synthesis and
recognition.
Speak
12
method
13
procedure Speak(const Text : string);
! Speaks the specified text to the user.
14
15
16
17
TApdSapiPhone Component 369
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
370 Chapter 10: SAPI Components
1
1
Chapter 11: Remote Access Service (RAS) Components
1
2
Async Professional provides two RAS components that make it easy to add RAS dialing
capability to your application. RAS dialing is performed via the Microsoft RAS API.
3
The TApdRasDialer provides an easy to use interface to the Microsoft Remote Access
Services API to establish a connection to another computer via Dialup Networking. In
addition, the component also provides a set of standard functions for manipulating
phonebook entries and displaying dial status information.
4
The TApdRasStatus component provides a dialing status dialog for use on machines whose
RAS DLL does not provide a status display.
5
Overview
6
Remote Access Services (RAS) is the Windows service that handles dial-up networking
connections via modem. RAS is installed by default on most Win9x machines. On NT
machines, however, RAS is not installed until you add a modem device to the system
configuration. On Windows 9x/ME, dialup-networking automatically starts when an
application attempts to connect to a remote machine. Under Windows NT, however, this
does not occur and it’s up to the user to explicitly dial with RAS.
7
8
9
Key to RAS operations is Window’s concept of a phonebook. An entry in the phonebook
contains the user’s dialup networking connection settings which includes the modem to use,
the phone number to dial, the network connection settings, and so on. In Windows NT, a
phonebook is contained in a phonebook file (a file with a .PBK extension) and there can be
more than one phonebook. In Windows 9x/ME only one phonebook exists and is stored in
the registry.
10
11
12
13
14
15
16
17
371
1
1
1
TApdRasDialer Component
2
The TApdRasDialer provides an interface to Microsoft’s Remote Access Service (RAS) API.
The component is used primarily to establish and terminate a connection to a remote
machine using Window’s dial-up networking, however it can also be used to manipulate
RAS phonebook entries and enumerate active connections.
3
4
5
6
7
8
9
The TApdRasDialer requires that RAS has been installed on the machine that the
application is to be run. If RAS is not installed, then an exception ecRasLoadFail is raised
whenever a TApdRasDialer function is called.
Dialing is performed by the Dial and DialDlg (WinNT) methods. Both asynchronous and
synchronous dialing options are available with Dial. The Hangup method terminates the
call. Phonebook entries are manipulated by the CreatePhonebookEntry,
EditPhonebookEntry, ListEntries, and PhonebookDlg (WinNT) methods. The dialing
parameters for a particular call can be obtained and by the GetDialParameters, and
SetDialParameters methods. For WinNT machines, a monitor can be displayed by
MonitorDlg to display the status of RAS connections.
Hierarchy
TComponent (VCL)
! TApdBaseComponent (OOMisc) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
10
11
TApdCustomRasDialer (AdRas)
TApdRasDialer (AdRas)
12
13
14
15
16
17
372 Chapter 11: Remote Access Service (RAS) Components
1
1
Properties
1
CallBackNumber
DialOptions
PlatformID
CompressionMode
Domain
SpeakerMode
Connection
EntryName
StatusDisplay
ConnectState
HangupOnDestroy
UserName
DeviceName
Password
DeviceType
Phonebook
DialMode
PhoneNumber
! Version
2
3
4
Methods
CreatePhonebookEntry
GetDialParameters
ListEntries
DeletePhonebookEntry
GetErrorText
MonitorDlg
Dial
GetStatusText
PhonebookDlg
DialDlg
Hangup
SetDialParamters
EditPhonebookEntry
ListConnections
6
7
8
Events
OnConnected
OnDialStatus
OnDialError
OnDisconnected
9
10
11
12
13
14
15
16
17
TApdRasDialer Component 373
1
1
1
Reference
CallBackNumber
property
2
property CallBackNumber : string
3
! Specifies a callback number for the current phonebook entry.
4
An empty string indicates that callback should not be used. This string is ignored unless the
user has “Set By Caller” callback permission on the RAS server. An asterisk indicates that
the number stored in the phonebook should be used for callback.
5
CompressionMode
property
property CompressionMode : TApdRasCompressionMode
6
7
8
TApdRasCompressionMode = (
cmDefault, cmCompressionOn, cmCompressionOff);
! Specifies whether software compression is to be used.
If the application is not running under Windows NT, this property is ignored.
Connection
9
10
run-time, read-only property
property Connection : TRasConnHandle
! Contains the RAS connection handle for the current phonebook entry.
This property is available if needed, but will probably not be of use to the user.
11
ConnectState
12
property ConnectState : Integer
13
14
! Contains connection state for the current phonebook entry.
Use ConnectState to determine the connect status for the current phonebook entry. This
value can be passed to GetStatusText to obtain the corresponding text string. Connection
state constants are defined in ADRASCS.INC
See also: GetStatusText
15
16
17
374 Chapter 11: Remote Access Service (RAS) Components
1
1
property
CreatePhonebookEntry
method
1
function CreatePhonebookEntry : Integer;
! Creates a new entry in the current phonebook.
2
A dialog box is displayed in which the user enters the information for the new entry.
A return value other than ecOK indicates that an error occurred and the return value is the
error code. This value can then be passed to GetErrorText to obtain a description of the
error.
3
4
See also: DeletePhonebookEntry, EditPhonebookEntry, GetErrorText
DeletePhonebookEntry
method
6
function DeletePhonebookEntry : Integer;
! Deletes the current phonebook entry.
If the EntryName property specifies an existing phonebook entry, that entry will be deleted
and the EntryName property set to an empty string. If a connection exists for the entry, it
will be disconnected.
A return value other than ecOK indicates that an error occurred and the return value is the
error code. This value can then be passed to GetErrorText to obtain a description of the
error.
See also: CreatePhonebookEntry, EditPhonebookEntry, EntryName, GetErrorText
DeviceName
7
8
9
10
run-time, read-only property
11
property DeviceName : string
! Contains the device name for the current active connection.
12
DeviceName contains the name of the current device, if available. This could be the name of
the modem, the name of the PAD, or the name of a switch device.
13
See also: ConnectState, DeviceType
14
15
16
17
TApdRasDialer Component 375
1
1
1
DeviceType
run-time, read-only property
property DeviceType : string
2
! Contains the device type name for the current active connection.
3
DeviceType contains a string that identifies the type of the current device, if available.
Common device types supported by RAS remote access service are “modem”, “pad”,
“switch”, “ison”, or “null.”
4
See also: ConnectState, DeviceName
5
Dial
method
function Dial : Integer;
6
7
8
9
10
11
12
13
14
15
! Establishes a Remote Access Service (RAS) connection.
Use Dial to establish a Remote Access Service (RAS) connection between a RAS client and a
RAS server. If a connection error occurs then the connection is automatically hung up.
During asynchronous dialing (DialMode = dmAsync), Dial returns immediately before the
connection is established. The connection progress is communicated via the OnDialStatus,
OnDialError, and OnConnected events. Additionally, if a TApdRasStatus component is
specified by StatusDisplay, then the status component displays a dialing status dialog until
the connection has been established or cancelled.
During synchronous dialing (DialMode is set to dmSync), Dial does not return until the
connection attempt has completed successfully or failed. No events are fired, so the Dial
function result must be checked to determine the connection status. A return value other
than ecOK indicates that an error occurred and the return value is the error code. This value
can then be passed to GetErrorText to obtain a description of the error.
Connection status information is also available via the ConnectState property until the
application calls HangUp to terminate the connection. An application must eventually call
HangUp after a connection has been successfully established.
Dial does not display a logon dialog box. This is currently done through the Remote
Networking application. Your application is responsible for setting the dialing properties.
See also: ConnectState, DialMode, GetErrorText, Hangup, OnConnected, OnDialError,
OnDialStatus, StatusDisplay
16
17
376 Chapter 11: Remote Access Service (RAS) Components
1
1
DialDlg
method
1
function DialDlg : Integer;
! Establishes a RAS connection using synchronous Ras dial dialog (WinNT).
2
If the application is not running under Windows NT, DialDlg will return a
ecFunctionNotSupported error.
3
DialDlg commences synchronous dialing and displays dialog boxes during the connection
operation to provide feedback to the user about the progress of the operation. The dialog
boxes also provide a Cancel button for the user to terminate the connection.
4
DialDlg returns when the connection is established, or when the user cancels the operation.
No progress events are fired so the function return value must be examined to determine the
status of the connection. A return value other than ecOK indicates that an error occurred
and the return value is the error code. This value can then be passed to GetErrorText to
obtain a description of the error. If a connection error occurs, the connection is
automatically hung-up.
6
7
Connection status information is also available via the ConnectState property until the
application calls HangUp to terminate the connection. An application must eventually call
HangUp after a connection has been successfully established.
8
See also: ConnectState, DialMode, GetErrorText, Hangup
9
DialMode
property
10
property DialMode : TApdRasDialMode
11
TApdRasDialMode = (dmSync, dmAsync);
Default: dmAsync
12
! Specifies whether dialing is performed asynchronously or not.
When asynchronous dialing is specified, a call to the Dial method returns immediately and
connection status is provided via the OnDialStatus and OnConnected events. When
synchronous dialing is specified, a call to the Dial method does not return until either a
connection has been established, or an error has been detected during dialing.
13
14
See also: Dial, OnConnected, OnDialStatus
15
16
17
TApdRasDialer Component 377
1
1
1
DialOptions
property
property DialOptions : TApdRasDialOptions
2
3
4
TApdRasDialOption = (doPrefixSuffix, doPausedStates,
doDisableConnectedUI, doDisableReconnectUI,
doNoUser, doPauseOnScript);
TApdRasDialOptions = set of TApdRasDialOption;
! Specifies dialing options for the current phonebook entry.
If the application is not running under Windows NT, this property is ignored.
5
6
Domain
property Domain : string
! Specifies a string containing the domain on which authentication is to occur.
7
An empty string specifies the domain in which the remote access server is a member. An
asterisk specifies the domain stored in the phonebook for the entry.
8
EditPhonebookEntry
9
function EditPhonebookEntry : Integer;
! Edits the current phonebook entry.
A dialog box is displayed in which the user can modify the information for the entry
specified by the EntryName property.
11
A return value other than ecOK indicates that an error occurred and the return value is the
error code. This value can then be passed to GetErrorText to obtain a description of the
error.
See also: CreatePhonebookEntry, DeletePhonebookEntry, EntryName, GetErrorText
13
EntryName
14
property EntryName : string
15
property
! Specifies a string containing the phonebook entry to use to establish a connection.
An empty string specifies a simple modem connection on the first available modem port, in
which case PhoneNumber must contain the number to be dialed.
16
17
378 Chapter 11: Remote Access Service (RAS) Components
1
method
10
12
1
property
GetDialParameters
method
1
function GetDialParameters : Integer;
! Retrieves stored dialing parameters.
2
The connection information saved by the last successful call to Dial or SetDialParameters
for the current phonebook entry is retrieved.
A return value other than ecOK indicates that an error occurred and the return value is the
error code. This value can then be passed to GetErrorText to obtain a description of the
error.
3
4
See also: GetErrorText, SetDialParameters
GetErrorText
method
6
function GetErrorText(Error : Integer) : string;
! Returns the text string for the specified RAS error.
7
Use GetErrorText to obtain the text for a given RAS error.
8
Example:
with ApdRasDialer1 do
ShowMessage(GetErrorText(DialDlg));
9
Error code constants are defined in ADRASEC.INC
10
See also: OnDialError
GetStatusText
method
11
function GetStatusText(State : TRasState) : string;
12
! Returns the text string for the specified RAS connection state.
Use GetStatusText to obtain the text for a given RAS connection state.
13
Example:
14
with ApdRasDialer1 do
ShowMessage(GetStatusText(ConnectState));
Connection state constants are defined in ADRASCS.INC
15
See also: ConnectState, OnDialStatus
16
17
TApdRasDialer Component 379
1
1
1
Hangup
method
procedure Hangup;
2
! Terminates the active RAS connection.
3
Hangup releases all RASAPI32.DLL resources associated with the connection. Hangup
must be called eventually if a connection has been made by calling either the Dial or DialDlg
methods. It is safe to call Hangup at any time during dialing.
4
See also: Dial, DialDlg
5
HangupOnDestroy
property
property HangupOnDestroy : Boolean
6
Default: True
! Specifies whether or not an active connection is terminated when the component is
7
8
9
10
11
destroyed.
If an active RAS connection has been established by calling either Dial or DialDlg, a
connection handle is maintained for the duration of the call and must be used to hangup the
call. By default, when the dialing application terminates, it will disconnect an active
connection since the connection handle is not saved. By setting HangupOnDestroy to True,
you can override this behavior and allow the connection to remain open when the
application closes.
ListConnections
method
function ListConnections(List : TStrings) : Integer;
! Obtains a list of all active RAS connections.
12
13
14
15
16
Use ListConnections to obtain a string list of all active RAS connections. The List argument
must be created before calling the function. Upon return, the List.Strings property contains
the entry names of the active connections. If List has not been created, an exception is
raised.
Example:
...
ComboBox1.Clear;
ApdRasDialer1.ListConnections(ComboBox1.Items);
A return value other than ecOK indicates that an error occurred and the return value is the
error code. This value can then be passed to GetErrorText to obtain a description of the
error.
17
380 Chapter 11: Remote Access Service (RAS) Components
1
1
ListEntries
method
1
function ListEntries(List : TStrings) : Integer;
! Obtains a list of all entry names in the current RAS phonebook.
2
Use ListEntries to obtain a string list of all entries in the current RAS phonebook. The List
argument must be created before calling the function. Upon return, the List.Strings
property contains the entry names contained in the phonebook. If List has not been created,
an exception is raised.
3
4
Example:
...
ComboBox1.Clear;
ApdRasDialer1.ListEntries(ComboBox1.Items);
...
ApdRasDialer1.EntryName :=
ComboBox1.Items[ComboBox1.ItemIndex];
...
6
7
A return value other than ecOK indicates that an error occurred and the return value is the
error code. This value can then be passed to GetErrorText to obtain a description of the
error.
8
See also: GetErrorText
9
MonitorDlg
method
10
function MonitorDlg : Integer;
! Displays the status of a connection using the Ras status dialog (WinNT).
Use MonitorDlg to display a a dialog showing the status of a connection. This is the same
dialog box you see if you right-click the Dial-Up Networking Monitor application in NT’s
system tray.
A return value other than ecOK indicates that an error occurred and the return value is the
error code. This value can then be passed to GetErrorText to obtain a description of the
error.
11
12
13
14
See also: GetErrorText
15
16
17
TApdRasDialer Component 381
1
1
1
OnConnected
event
property OnConnected : TApdRasConnectedEvent
2
TApdRasConnectedEvent = procedure(Sender : TObject) of object;
! Defines an event handler that is called when a RAS connection has been established.
3
4
During asynchronous dialing (DialMode = dmAsync), OnConnected is called to notify that
the connection has been successfully established. If StatusDisplay specifies a TApdRasStatus
dialog, the dialog is closed.
5
During synchronous dialing (DialMode = dmSync), this event is not used and the
ConnectState property should be read to determine if a connection has been established.
See also: ConnectState, Dial, DialDlg, DialMode
6
7
8
OnDialError
event
property OnDialError : TApdRasErrorEvent
TApdRasErrorEvent = procedure(
Sender : TObject; Error : Integer) of object;
! Defines an event handler that is called when an error occurs during dialing.
9
10
OnDialError is called whenever an error occurs while attempting to complete a RAS
connection. If no event handler is defined, the result of the called method should be used to
determine if an error has occurred.
The Error parameter may be passed to GetErrorText to obtain the corresponding error text.
11
Error code constants are defined in ADRASUTL.PAS
See also: GetErrorText
12
13
14
15
16
17
382 Chapter 11: Remote Access Service (RAS) Components
1
1
OnDialStatus
event
1
property OnDialStatus : TApdRasStatusEvent
2
TApdRasStatusEvent = procedure(
Sender : TObject; State : TRasConnState) of object;
! Defines an event handler that is called to provide status during dialing.
3
During asynchronous dialing (DialMode = dmAsync), OnDialStatus is called periodically
to provide connection status information. During synchronous dialing (DialMode =
dmSync), this event is not used and the ConnectState property should be read to determine
the connection status.
4
The State parameter may be passed to GetStatusText to obtain the corresponding text string.
Connection state constants are defined in ADRASUTL.PAS
6
See also: ConnectState, Dial, DialMode, GetStatusText
OnDisconnected
event
property OnDisconnected : TNotifyEvent
7
8
! Defines an event handler that is called when an active connection is terminated.
If an active RAS connection has been established by calling either Dial or DialDlg, the
OnDisconnected event is fired when the connection is closed.
9
10
See also: ConnectionStatus, OnConnected
Password
property
11
property Password : string
! Specifies a string containing the user’s password.
12
Password is used to authenticate the user’s access to the remote access server.
13
See also: UserName
14
15
16
17
TApdRasDialer Component 383
1
1
1
Phonebook
property
property Phonebook : string
2
! Specifies the full path and filename of the phonebook file.
3
For applications running under Windows NT, Phonebook can be an empty string, and
typically is, in which case the default phonebook file RASPHONE.PBK is used.
4
For applications running under Windows 95/98, Phonebook is ignored. Dial-up networking
stores phonebook entries in the registry rather than in a phonebook file.
5
PhonebookDlg
method
function PhonebookDlg : Integer;
6
7
8
9
10
11
! Displays the main Dial-Up Networking dialog box. (WinNT).
PhonebookDlg displays the main Dial-Up Networking modal dialog box from which the
user can edit, or delete a selected phonebook entry, create a new phonebook entry, or
specify user preferences.
A return value other than ecOK indicates that an error occurred and the return value is the
error code. This value can then be passed to GetErrorText to obtain a description of the
error.
See also: GetErrorText
PhoneNumber
property PhoneNumber : string
! Specifies a string containing an overriding phone number.
12
An empty string indicates that the phonebook entry’s phone number should be used. If
EntryName contains an empty string, PhoneNumber must contain a phone number to dial.
13
See also: EntryName
14
15
16
17
384 Chapter 11: Remote Access Service (RAS) Components
1
1
property
PlatformID
run-time, read-only property
1
property PlatformID : DWord
! Identifies the platform supported by the operating system.
2
PlatformID can have one of the following values:
Value
Operating System
VER_PLATFORM_WIN32_WINDOWS
Windows 95/98/ME
VER_PLATFORM_WIN32_NT
Windows NT
3
4
SetDialParameters
method
function SetDialParameters : Integer;
6
! Updates stored dialing parameters.
This function changes the connection information saved by the last call to Dial or
SetDialParameters for the current phonebook entry. Only the UseName, Password, Domain
name, and Callback number can be changed.
A return value other than ecOK indicates that an error occurred and the return value is the
error code. This value can then be passed to GetErrorText to obtain a description of the
error.
7
8
9
See also: GetDialParameters, GetErrorText
SpeakerMode
property
10
11
property SpeakerMode : TApdRasSpeakerMode
TApdRasSpeakerMode = (smDefault, smSpeakerOn, smSpeakerOff);
! Specifies the modem speaker setting for the current phonebook entry.
For applications not running under Windows NT, this property is ignored.
12
13
14
15
16
17
TApdRasDialer Component 385
1
1
1
StatusDisplay
property
property StatusDisplay : TApdRasStatus
2
! Specifies an attached dialing status dialog.
3
The StatusDisplay provides a mechanism to display a TApdRasStatus dialog while
establishing a connection via the Dial function. If the DialDlg function is used then this
property is ignored.
4
See also: Dial, DialDlg
5
UserName
property UserName : string
6
! Specifies a string containing the user’s identification name.
UserName is used to authenticate the user’s access to the remote access server.
7
See also: Password
8
9
10
11
12
13
14
15
16
17
386 Chapter 11: Remote Access Service (RAS) Components
1
1
property
TApdRasStatus Component
1
The TApdRasStatus provides a standard RAS dialing status dialog with a Cancel button to
abort dialing at any time. To use it, just create an instance and assign it to the StatusDisplay
property of your TApdRasDialer component.
2
TApdRasStatus has no methods that you must call or properties that you must adjust. You
might want to change the settings of the Ctl3D and Position properties to modify the
appearance and placement of the window.
3
4
Figure 11.1 shows the display that is associated with a TApdRasStatus component.
6
7
Figure 11.1: TApdRasStatus component display.
8
Hierarchy
9
TComponent (VCL)
TApdBaseComponent (OOMisc) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
10
TApdCustomRasStatus (AdRas)
TApdRasStatus (AdRStat)
11
12
13
14
15
16
17
TApdRasStatus Component 387
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
388 Chapter 11: Remote Access Service (RAS) Components
1
1
Chapter 12: TAPI Components
1
2
The Telephony Application Programming Interface (TAPI) is a collection of DLLs and a
documented programming interface for centralizing and controlling telephony
communications services. TAPI was developed by Microsoft primarily for Computer
Telephony Integration (CTI) applications. TAPI provides the services that telephone
equipment and system providers need to integrate Windows programming and telephone
hardware.
3
4
TAPI also provides a smaller, though much more visible, service in managing modems as
system devices. This is a tremendous boon to communications programmers. No longer do
communications applications need to search serial ports for modems, try to identify
modems, burden the user with questions about their modem, or try any of the other
traditional approaches to supporting modems. Under TAPI, that task is handled by the
operating system. Programs make a few simple TAPI calls to determine what modems are
available.
5
6
7
Another advantage of TAPI is that applications can share serial ports. For example, assume
that an application opens a TAPI device to accept incoming fax or data calls. A second
application, if it also uses TAPI, can safely open the same TAPI device for an outgoing call.
When the outgoing call is over, TAPI resumes monitoring incoming calls without any
further action required by either application.
8
9
The TAPI components do have some drawbacks. TAPI may not be installed, or properly
configured, on all operating systems. TAPI relies upon several device drivers and INF files to
properly configure the modems. If these supporting files are not present, or if they are not
adequate, TAPI operations may fail or behave erratically. Consider making a data
connection through TAPI; if the wrong modem driver is selected for an installed modem,
TAPI could configure the modem to a state where it is incompatible with the remote device.
The modem driver may also be written poorly, most notably by not requiring responses to
modem commands.
10
11
12
13
The second major disadvantage of TAPI is that it doesn’t provide the level of visibility or
control that is provided by TAdModem. TAdModem provides more detailed status
information and more control over failures such as “no dialtone” and “called number busy”
conditions. It is also much easier to customize TAdModem to provide unique
configurations.
14
15
The third major disadvantage of TAPI is that TAPI doesn’t provide support for direct,
modemless connections. TAPI applications usually must still include logic for the direct
opening of serial ports.
16
17
389
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
The fourth major disadvantage of TAPI is the difficulty of assuring proper modem
configuration. TAPI modems are detected and installed by Windows during the installation
process. If a new modem is added later, the instructions provided with the modem usually
direct the user to run one of the Control Panel applets “Add New Hardware” or “Modems.”
The “Add New Hardware” applet found in Windows scans all available serial ports for
attached modems. When it finds a modem, it sends a variety of commands to the modem
and compares the responses against a large database of known modems, usually resulting in
an unambiguous choice. The “Modems” applet (or a setup program provided by the
modem vendor), can skip this detection process if the modem type is already known.
No matter how the modem is installed, the end result is that TAPI now knows everything it
needs to about that modem (serial port, baud rate, and the specific configuration
commands/responses). Property sheets are available to the user for changing the
configuration of the modem. For example, the user can turn the speaker on or off, change
the attached serial port, enable/disable flow control, and so on.
The default property values for the modem chosen by Microsoft or the modem vendor are
the values that provide the best results in the widest variety of situations. These properties,
however, are available to the user via the Modem applet’s modem property sheets. If the user
changes a critical value (say, the serial port number or perhaps flow control) it’s likely that
your application won’t operate properly when using that modem. Unfortunately, there isn’t
much you can do to protect against this. The responsibility for assuring the modem is
properly configured is in the user’s hands, not the application.
The final major disadvantage of TAPI is that resources often do not get properly released
following an abnormal termination of an application using TAPI. To counter this problem,
TApdTapiDevice includes a TAPI crash recovery mechanism which will automatically detect
this situation and release TAPI resources.
All of these issues are likely to improve over time, making TAPI the choice for current and
future communications programs. The best recommendation at this point is to use TAPI for
applications targeted for Windows.
Async Professional provides components that make use of TAPI modem management.
These components provide an alternate to TAdModem for configuring modems and dialing
and answering calls. Async Professional negotiates for TAPI 1.4 in Windows applications.
Async Professional also supports TAPI 1.3 functions in 16-bit applications for Windows 3.1
and Windows for Workgroups, but those environments lack a general purpose modem
service provider, which renders most of the TAPI functions useless for serial
communications programs.
16
17
390 Chapter 12: TAPI Components
1
1
The Async Professional TAPI components provide all the services necessary for selecting
TAPI devices, dialing and answering calls, and receiving status information about TAPI calls
in progress. The TAPI system itself provides even more services. For more information
about TAPI, see the following sources:
1
2
• Sells, Windows Telephony Programming: A Developer’s Guide to TAPI,
Addison-Wesley, ISBN 0/201/63450-3.
3
• TAPI Reference Manual, included with the TAPI SDK and with the Windows 95/98
4
SDK.
• “Create Communications Programs for Windows 95 with the Win32 Comm API”,
Microsoft Systems Journal, 1994 #12 (December).
• Programming Windows 95 Unleashed (SAMS Publishing). Although only one out of
37 chapters is devoted to TAPI, it provides a nicely condensed version of much of the
information in the TAPI Reference Manual, plus some helpful C++ example
programs.
• The C++ sample program TAPICOMM, available on the Microsoft Developer
Network CD.
6
7
8
9
10
11
12
13
14
15
16
17
Chapter 12: TAPI Components 391
1
1
1
TAPI Device Control from an Application
2
Without TAPI, the TApdComPort component opens the physical serial port directly using
the appropriate Windows API call, which returns a handle to that port. The TApdComPort
then uses the handle to send and receive data and otherwise control the serial port.
Configuring, dialing or answering the modem requires sending explicit ATXxx commands
to the modem, interpreting the responses, and dealing with the myriad of differences among
currently available modems.
3
4
5
6
7
8
9
10
11
12
13
14
15
16
With TAPI, a TApdComPort is still required, but it is not initially involved in establishing
the modem connection—a TApdTapiDevice is used for that purpose. The application calls
Dial to place an outgoing call or calls AutoAnswer to wait for an incoming call. TAPI sends
the appropriate ATXxx commands, interprets the responses, and establishes the modem
connection.
Once the connection is established, TAPI’s role is essentially over. TAPI remains in charge of
the call until the modem connection is broken, but the TApdTapiDevice is not used again.
From this point on you use the TApdComPort to control the port and send/receive data, just
as if TAPI is not involved. The TApdComPort is automatically opened by the
TApdTapiDevice when the modem connection is established. Several TApdComPort
properties are updated with appropriate information from the TAPI device, notably Baud
and ComNumber.
When the modem connection is broken, TApdTapiDevice automatically closes the
associated TApdComPort. The TApdComPort cannot be used for input/output unless the
modem connection is re-established by the TAPI device or unless the program bypasses
TAPI and opens and uses the TApdComPort directly.
The TApdComPort property TapiMode determines whether the port is in charge of the
physical serial port or whether TAPI is in charge of the port:
TapiMode : TTapiMode;
TTapiMode = (tmNone, tmAuto, tmOn, tmOff);
When TapiMode is tmAuto (the default), the TApdComPort is in charge unless a
TApdTapiDevice is added to the form. The TApdTapiDevice automatically forces the
TapiMode property to tmOn and thereafter the TApdTapiDevice is in charge. Attempts to
open the TApdComPort directly (by setting its Open property to True) are ignored.
You can give control back to the TApdComPort by setting TapiMode to tmOff, meaning that
the port is not to be used in TAPI mode even if a TApdTapiDevice is present. To give control
back to the TApdTapiDevice again, set TapiMode to tmAuto or tmOn.
17
392 Chapter 12: TAPI Components
1
1
TAPI events
1
TAPI dials outgoing calls and waits for incoming calls in the background. Applications are
informed of progress through a callback procedure. TApdTapiDevice installs a hidden
callback and translates these progress callbacks into the following VCL events:
2
OnTapiStatus
3
procedure(
CP : TObject; First, Last : Boolean; Device, Message,
Param1, Param2, Param3 : DWORD) of object;
Generated at various intervals while dialing an outgoing call or answering an incoming call.
The parameters mirror the parameters passed directly to the TAPI callback. You will usually
need to reference only the Message and Param1 fields. The other fields are supplied for
applications that extend the services provided by TApdTapiDevice. See the OnTapiStatus
event on page 425. Also see “TAPI status processing” on page 394.
OnTapiLog
4
6
7
procedure(CP : TObject; Log : TTapiLogCode) of object;
Generated at the start and finish of each TAPI call (either outgoing or incoming) and at
various points during the call. See the OnTapiLog event on page 424. Also see “TAPI
logging” on page 397.
OnTapiPortOpen
procedure(CP : TObject) of object;
Generated immediately after TAPI has established a connection and has the serial port
handle available for handoff to the TApdComPort component. See the OnTapiPortOpen
event on page 425.
OnTapiPortClose
8
9
10
11
12
procedure(CP : TObject) of object;
Generated immediately after TAPI closes the serial port due to a broken connection. See the
OnTapiPortClose event on page 424.
13
OnTapiConnect
14
procedure(CP : TObject) of object;
Generated immediately after TAPI establishes a connection. See the OnTapiConnect event
on page 393.
15
16
17
TAPI Device Control from an Application 393
1
1
1
OnTapiFail
procedure(CP : TObject) of object;
2
Generated immediately after TAPI tries but fails to establish a connection. See the
OnTapiFail event on page 394.
3
TAPI status processing
4
TAPI handles the details of controlling, dialing, and answering the modem. The
OnTapiStatus event is provided to let you know what’s happening as the connection
progresses.
5
6
7
8
9
10
11
12
13
14
15
16
17
TAPI actually uses a callback procedure to inform an application program of its progress.
TApdTapiDevice installs a hidden callback and translates all calls into OnTapiStatus events
for easier processing. The format of the event handler is identical to the internal TAPI
callback. The following illustrates the format and use of OnTapiStatus:
...
ApdTapiDevice1: TApdTapiDevice;
Msg: TLabel;
Num: TLabel;
...
procedure ApdTapiDevice1TapiStatus(
CP : TObject; First, Last : Boolean; Device, Message, Param1,
Param2, Param3 : LongInt);
end;
procedure TForm1.ApdTapiDevice1TapiStatus(
CP : TObject; First, Last : Boolean; Device, Message,
Param1, Param2, Param3 : LongInt);
begin
if First then
...do setup stuff
else if Last then
...do cleanup stuff
else begin
{Update status}
Msg.Caption := ApdTapiDevice1.TapiStatusMsg(Message, Param2);
Num.Caption := ApdTapiDevice1.Number;
end;
end;
ApdTapiDevice1TapiStatus handles the OnTapiStatus event by updating a form at each call.
First is True for the first status event of the current call. Last is True for the last status event of
the current call. These parameters can be used to setup and cleanup forms and other
resources used when displaying status information.
394 Chapter 12: TAPI Components
1
1
The remaining parameters mirror the parameters that TAPI sends to the hidden callback
procedure. Only Message and Param1 are used by TApdTapiDevice. The other parameters
are provided in case you extend TApdTapiDevice beyond making and answering calls.
Async Professional contains resource strings for all of the values of Message and Param1 that
TApdTapiDevice can generate. Most applications can simply pass those parameters to
TapiStatusMsg to return the appropriate string, as shown in the code above. If returning an
appropriate string is sufficient for your purposes, you can skip the following discussion of
the parameters. Only Message and Param1 are described in detail; the rest are mentioned
only briefly.
1
2
3
4
Message is a constant that describes the class of change since the previous OnTapiStatus
event. The possible values are shown in Table 12.1.
Table 12.1: Possible TAPI messages
6
TAPI Message
Value
Explanation
Line_CallState
2
The state of the call changed.
Line_LineDevState
8
The device state changed.
Line_Reply
12
The previous request was accepted.
Line_APDSpecific
32
Async Professional-specific status.
The first three values are a subset of the possible TAPI messages. These are the only values
that dialing and answering generate. The final value, Line_APDSpecific isn’t really a TAPI
status message. It’s a pseudo state change that TApdTapiDevice generates to provide more
information about the progress of a call.
Line_CallState indicates that the progress of the call, what TAPI calls the “state” of the call,
changed. For example, the line was idle, but now it is dialing or the line was dialing but now
it is proceeding (the TAPI term for waiting for the connection).
7
8
9
10
11
12
Line_LineDevState indicates that the state of the device (modem, phone, or whatever)
changed. TApdTapiDevice dial/answer actions generate this message only to indicate that
the modem is ringing.
13
Line_Reply indicates that TAPI has accepted, but not necessarily completed, the requested
background. For example, it is generated just after a request to dial a number.
14
Line_APDSpecific is generated during periods when TAPI does not generate events, such as
after dialing and waiting for a connection, or when answering and waiting for a connection.
The primary purpose of Line_APDSpecific is to give the status event an opportunity to
update a timer.
15
16
17
TAPI Device Control from an Application 395
1
1
1
2
3
Param1 provides additional information about Message. For example, when Message is
Line_CallState, Param1 contains a constant describing the change in state. Table 12.2 shows
the values of Param1 that are generated by TApdTapiDevice for each value of Message.
Table 12.2: Corresponding Param1 values to Message values
Param1 Value
Explanation
For Message = Line_CallState
4
5
6
7
8
LineCallState_Idle
No call in progress.
LineCallState_Offering
Call starting.
LineCallState_Accepted
Incoming call accepted.
LineCallState_DialTone
Dialtone detected.
LineCallState_Dialing
Dialing the outgoing number.
LineCallState_Proceeding
Handshaking with the remote modem.
LineCallState_RingBack
Detected a remote ring.
LineCallState_Busy
Detected a busy signal.
LineCallState_Connected
Connected with the remote modem.
LineCallState_Disconnected
Disconnected from remote modem.
9
For Message = Line_DevState
10
LineDevState_Ringing
11
For Message = Line_Reply
Ring detected for incoming call.
(Param1 not used for Line_Reply)
12
13
14
15
16
For Message = Line_APDSpecific
APDSpecific_DialFail
Dial failed due to busy or other
error.
APDSpecific_RetryWait
Waiting for next retry.
APDSpecific_TAPIChange
Unknown state change.
These are only subsets of the possible values of Param1 for each of the Message states, but
they are the only values generated for the dial/answer actions performed by
TApdTapiDevice.
17
396 Chapter 12: TAPI Components
1
1
Other TApdTapiDevice properties can also be used in OnTapiStatus. Some examples are
Number, which contains the number just dialed, and SelectedDevice, which contains the
name of the TAPI device.
1
Automatic status display
2
Async Professional includes a mechanism for providing automatic TAPI status display
without programming, through the StatusDisplay property of TApdTapiDevice:
3
property StatusDisplay : TApdAbstractTapiStatus
The TApdAbstractTapiStatus class is described in more detail on page 440. For each
OnTapiStatus event, TApdTapiDevice checks whether StatusDisplay is assigned. If it is, the
UpdateDisplay method of StatusDisplay is called to update the display. TApdTapiDevice
then calls the OnTapiStatus event, if one is implemented.
When a TApdTapiDevice component is created, either dynamically or when dropped on a
form, it searches the form for a TApdAbstractTapiStatus instance and updates the
StatusDisplay property with the first one it finds. StatusDisplay is also filled in if a
TApdAbstractTapiStatus component is added to the form later. You can also change
StatusDisplay at design time or run time to point to a different TApdAbstractStatusDisplay
component.
TAPI logging
4
6
7
8
9
Dialing and answering calls is often an automated process. For example, an application
might automatically dial a list of numbers every night to upload or download the day’s
transaction files. Or, an application might serve a BBS-like role where customers dial in to
get the latest updated files or information.
10
11
12
13
14
15
16
17
TAPI Device Control from an Application 397
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
The TApdTapiDevice provides an event that is ideal for logging automated dial or answer
applications. The OnTapiLog event provides an opportunity to log information about each
call. The information that is logged includes the time the call starts, the time it ends, and
additional information about events during the call (connected, busy/retry, and so on). This
example that handles the OnTapiLog event:
procedure TForm1.ApdTapiDevice1TapiLog(
CP : TObject; Log : TTapiLogCode);
var
HisFile : Text;
begin
...open HisFile
{Write the log entry}
with TapiDevice do begin
case Log of
ltapiNone : ;
ltapiCallStart :
WriteLn(HisFile, DateTimeToStr(Now), ' : call started');
ltapiCallFinish :
WriteLn(HisFile, DateTimeToStr(Now), ' :
call finished'^M^J);
ltapiDial :
WriteLn(
HisFile, DateTimeToStr(Now), ' : dialing ', Number);
ltapiAnswer :
WriteLn(HisFile, DateTimeToStr(Now), ' : answering');
ltapiConnect :
WriteLn(HisFile, DateTimeToStr(Now), ' : connected');
ltapiCancel :
WriteLn(HisFile, DateTimeToStr(Now), ' : cancelled');
ltapiDrop :
WriteLn(HisFile, DateTimeToStr(Now), ' : dropped');
ltapiBusy:
WriteLn(HisFile, DateTimeToStr(Now), ' : busy');
ltapiDialFail :
WriteLn(HisFile, DateTimeToStr(Now), ' : dial failed');
end;
end;
...close HisFile
end;
16
17
398 Chapter 12: TAPI Components
1
1
This example shows every possible logging value. The meanings of the various logging
conditions are described in Table 12.3.
Table 12.3: TAPI logging conditions
1
2
Logging Condition
Explanation
ltapiAccept
Accepting an incoming call.
ltapiAnswer
An incoming ring was detected and the call is being
answered.
ltapiBusy
The called number was busy.
ltapiCallFinish
The call is finished, either successfully or due to
an error.
ltapiCallStart
A call, either answer or dial, was started.
ltapiCancel
The dial or answer operation was cancelled before a
connection was established.
ltapiConnect
A successful modem connection was established after
a dial or answer.
7
ltapiDial
An outgoing call was just dialed. The Number
property contains the number that was dialed.
8
ltapiDialFail
A modem connection was not established due to no
dialtone, no answer, or some other error.
9
ltapiDrop
A connection was established but was subsequently
dropped. The drop can be due to an error or due to
the normal completion of the session.
ltapiReceivedDigit
A DTMF digit has been received.
A call is always started with an ltapiCallStart event and finished with an ltapiCallFinish
event. There may be one or more other events in between. A typical dial operation might
generate the following sequence of log events:
ltapiCallStart
ltapiDial
ltapiBusy
ltapiDial
ltapiConnect
ltapiDrop
ltapiCallFinish
call started
number dialed
called number was busy
number re-dialed
a connection was established
the connection was dropped
the call is finished
3
4
6
10
11
12
13
14
15
16
17
TAPI Device Control from an Application 399
1
1
1
2
3
4
Automatic TAPI logging
Async Professional includes a mechanism for providing automatic TAPI logging without
programming, through the TapiLog property of the TApdTapiDevice:
property TapiLog : TApdTapiLog
The TApdTapiLog class is described in more detail on page 445. For each OnTapiLog event,
TApdTapiDevice checks whether TapiLog is assigned. If it is, the UpdateLog method of
TapiLog is called to update the log. TApdTapiDevice then calls the OnTapiLog event, if one
is implemented.
6
When a TApdTapiDevice component is created, either dynamically or when dropped on a
form, it searches the form for a TApdTapiLog instance and updates the TapiLog property
with the first one it finds. TapiLog is also filled in if a TApdTapiLog component is added to
the form later. You can also change TapiLog at design time or run time to point to a different
TApdTapiLog component.
7
Making calls
8
TApdTapiDevice provides a method for placing outgoing calls. When Dial is called, TAPI
sends the appropriate modem configuration commands to the modem, then dials the
number passed to the Dial method.
5
9
10
11
12
13
14
15
16
The number passed to Dial should not contain any modem commands. It should contain
the telephone number to dial, exactly as it would be dialed from a telephone handset.
TApdTapiDevice generates OnTapiStatus events during the dialing process. If a connection
is not established, an OnTapiFail event is generated. If a connection is established, an
OnTapiConnect event is generated, the associated TApdComPort is opened, and the
OnTapiPortOpen event is generated.
Once the connection is established, the TApdTapiDevice is no longer directly used. All
subsequent port control and input/output operations use the TApdComPort. The exception
to this occurs when the call is terminated. The application should not simply close the
TApdComPort because that would not disconnect the modem connection. Instead, the
application must direct TAPI to close the connection by calling the CancelCall method of
TApdTapiDevice. The TApdTapiDevice then breaks the connection, closes the associated
TApdComPort, and generates the OnTapiPortClose event.
If a dial attempt fails due to a busy signal or other error, TApdTapiDevice can try the call
again. This is controlled by the MaxAttempts property, which determines how many times
Dial tries the call, and RetryWait, which determines how long (in seconds) Dial waits before
retrying a failed call.
17
400 Chapter 12: TAPI Components
1
1
Dialing example
1
This example demonstrates how to construct and use the TApdTapiDevice to dial a number.
This example includes a terminal window and emulator so that you can dial and log on to a
BBS or information service.
2
Create a new project, add the following components, and set the property values as
indicated in Table 12.4.
3
Table 12.4: Example components and property values
4
Component
Property
TApdComPort
TAdEmulator
6
TAdTerminal
TApdTapiDevice
SelectedDevice
7
TApdTapiStatus
TApdTapiLog
TButton
8
Name
Double-click on the Dial button’s OnClick event handler within the Object Inspector and
modify the generated method to match this:
procedure TForm1.DialClick(Sender : TObject);
begin
ApdTapiDevice1.Dial('1-847-262-6000');
end;
The phone number passed to Dial is the number of the U.S. Robotics BBS. Modify it if you
want to dial a different BBS or service. This method tells TAPI to initialize the modem and
dial the number.
Next, double-click on the Hangup button’s OnClick event handler within the Object
Inspector and modify the generated method to match this:
procedure TForm1.HangupClick(Sender : TObject);
begin
ApdTapiDevice1.CancelCall;
end;
This method cancels the dial operation, or hangs up the phone after the connection is
established.
9
10
11
12
13
14
15
16
17
TAPI Device Control from an Application 401
1
1
1
2
3
4
Finally, double-click on the OnTapiPortOpen event handler within the Object Inspector and
modify the generated method to match this:
procedure TForm1.ApdTapiDevice1TapiPortOpen(Sender : TObject);
begin
ApdTerminal1.SetFocus;
end;
This event handler gives the focus to the terminal window as soon as TAPI establishes a
connection with the BBS.
This example is in the EXTAPID project in the \ASYNCPRO\EXAMPLES directory.
5
Answering calls
6
The process of answering the modem is very similar to dialing. The TApdTapiDevice
component generates the same events as it does when dialing.
7
8
9
10
11
12
The TApdTapiDevice waits for incoming calls in the background. No events are generated
while waiting for calls. When an incoming call is detected, TApdTapiDevice begins
generating OnTapiStatus events at regular intervals. If a connection is not established, an
OnTapiFail event is generated. If a connection is established, an OnTapiConnect event is
generated, the associated TApdComPort is opened, and the OnTapiPortOpen event is
generated.
Once the connection is established, the TApdTapiDevice is no longer directly used. All
subsequent port control and input/output operations use the TApdComPort. The exception
to this occurs when the call is terminated. The application should not simply close the
TApdComPort because that would not disconnect the modem connection. Instead, the
application must direct TAPI to close the connection by calling the CancelCall method of
TApdTapiDevice. The TApdTapiDevice then breaks the connection, closes the associated
TApdComPort, and generates the OnTapiPortClose event.
Answering example
13
The following example demonstrates how to construct and use the TApdTapiDevice to
answer an incoming call.
14
15
16
17
402 Chapter 12: TAPI Components
1
1
Create a new project, add the following components, and set the property values as
indicated in the Table 12.5.
Table 12.5: Example components and property values
Component
Property
1
2
Value
3
TApdComPort
TAdEmulator
4
TAdTerminal
TApdTapiDevice
SelectedDevice
<set as needed for your PC>
TButton
Name
Answer
TButton
Name
Hangup
TApdTapiStatus
TApdTapiLog
6
7
Double-click on the Answer button’s OnClick event handler within the Object Inspector and
modify the generated method to match this:
procedure TForm1.AnswerClick(Sender : TObject);
begin
ApdTapiDevice1.AutoAnswer;
end;
AutoAnswer instructs TAPI to listen for incoming calls. It does not immediately begin
answering an incoming call. In fact, if an incoming call is ringing before you call
AutoAnswer, AutoAnswer will most likely not pick up that call. This happens because TAPI
alerts applications of an incoming call on the first ring of that call. If no applications are
listening at that point, TAPI does not attempt to answer the call.
Next, double-click on the Hangup button’s OnClick event handler within the Object
Inspector and modify the generated method to match this:
procedure TForm1.HangupClick(Sender : TObject);
begin
ApdTapiDevice1.CancelCall;
end;
This method tells TAPI to stop listening for incoming calls. If a call is in the process of being
answered, it is aborted immediately. If a connection was already established, it is
disconnected.
8
9
10
11
12
13
14
15
16
17
TAPI Device Control from an Application 403
1
1
1
2
3
4
Finally, double-click on the OnTapiPortOpen event handler within the Object Inspector and
modify the generated method to match this:
procedure TForm1.ApdTapiDevice1TapiPortOpen(Sender : TObject);
begin
ApdTerminal1.SetFocus;
end;
This event handler gives the focus to the terminal window as soon as TAPI establishes a
connection.
This example is in the EXTAPIA project in the \ASYNCPRO\EXAMPLES directory.
5
TAPI Service Providers
6
TAPI itself doesn’t implement any of the features necessary for controlling serial ports and
telephony devices. The TAPI architecture dictates that the low-level, physical services are
provided by a TAPI Service Provider (TSP).
7
8
9
10
11
12
13
14
15
Even if TAPI is properly installed, it will not function unless a service provider is also
installed. TSP modules are typically provided by telephony vendors along with their
telephony hardware. Windows 95/98 and Windows NT 4.0 install a general-purpose service
provider named UNIMDM.TSP, which provides basic dial and answer support for modems.
It is this service provider that makes TAPI available to communications programs in
Windows.
Since UNIMDM.TSP is the service provider that your application is most likely to
encounter, it’s worth noting a few of its limitations here:
• UNIMDM does not provide support for caller identification (caller ID). The CallerID
property of TApdTapiDevice always returns an empty string when using UNIMDM.
• UNIMDM does not support “no dialtone” detection. TAPI will attempt to dial
whether a dialtone is detected or not.
Microsoft has released an extension for Unimodem called UNIMODEM/V.
UNIMODEM/V provides additional TAPI services (including support for caller ID and “no
dialtone” detection). As of this writing UNIMODEM/V is only for Windows 95/98.
UNIMODEM/V for Windows NT 4.0 is not available yet. See the README.TXT file for
updated news on UNIMDM supported services. You can also view the UNIMODEM/V
README.TXT file at http://support.microsoft.com/support/kb/articles/q140/3/23.asp.
" Caution: You cannot assume UNIMODEM/V is installed on your user’s machines since it
16
was released after the initial release of Windows 95.
17
404 Chapter 12: TAPI Components
1
1
Using TAPI for configuration only
1
Although UNIMDM.TSP provides basic dial and answer services it does not provide all of
the modem services an application might need. UNIMDM.TSP cannot be used to place a
faxmodem in fax answer mode or in adaptive answer mode (in which both incoming fax
and incoming data calls are accepted).
2
However, TAPI (along with UNIMDM and modem information files) contains a wealth of
configuration information and it is worthwhile to use TAPI to configure the modem and
control the call, even if TAPI doesn’t dial or answer the modem. This is provided by a feature
called “passthrough mode.” In passthrough mode, TAPI immediately enters the
“connected” state and opens the associated serial port.
4
Although in passthrough mode, TAPI doesn’t send any modem initialization commands,
TApdTapiDevice uses a two-step process to enter passthrough mode, which forces TAPI to
send its modem initialization commands. When the ConfigAndOpen method is called,
TApdTapiDevice first initializes TAPI in answer mode, which forces TAPI to send its
initialization commands. TApdTapiDevice then immediately closes the port and reopens it
in passthrough mode.
After calling ConfigAndOpen, TAPI is in control of the call, just as though it had dialed or
answered the modem. No OnTapiStatus or OnTapiLog events are generated, but
OnTapiPortOpen is generated. To close the call, use CancelCall. If TAPI ever aborts or closes
the call itself, the TApdTapiDevice generates the OnTapiPortClose event.
You should use TAPI passthrough mode if you need to support TAPI, but require modem
operations that UNIMDM.TSP doesn’t provide. An example of this arises in Async
Professional when a TAPI-based program needs to send or receive faxes. See EXTAPIF for
an example of a TAPI-based fax program.
Wave file support
3
6
7
8
9
10
11
12
The TApdTapiDevice class now includes the ability to play and record wave files through a
TAPI device (over the phone line). This feature, along with the new DTMF feature, allows
you to created an automated voice answering system with Async Professional. To play and
record wave files through the TAPI device, you must have the following:
13
• Windows 95/98/ME or Windows 2000.
14
• UNIMODEM/V or UNIMODEM/5.
15
• A voice modem with a wave driver.
16
• A wave file.
17
TAPI Device Control from an Application 405
1
1
1
2
3
4
5
6
7
8
9
10
UNIMODEM/V is a set of DLLs that provides voice support for voice modems under
Windows 95/98. Voice support includes DTMF tone detection and generation, and wave file
playback and recording. UNIMODEM/V is currently available only for Windows
95/98/ME. You can get UNIMODEM/V for Windows 95 from the Microsoft web site.
To use the voice extensions provided by UNIMODEM/V, you must have a voice modem. For
wave support, it is important that you have the wave driver for the modem installed. Consult
your modem documentation to install the wave device properly.
The TApdTapiDevice component allows you to set the wave file format used for playback
and recording. The default wave format is PCM, 8KHz, 16 bit, mono. This format was
chosen because it is supported by the majority of voice modems. Some voice modems
support other wave file formats.
Wave files used for playback with Async Professional can be created with the Microsoft
Sound Recorder program. Wave files for use with TAPI which will be played over general
telephone lines (POTS) must be recorded in a PCM format compatible with your voice
modem (here again, the attributes 8,000 Hz (8Khz), 16 Bit, mono are a good bet). Sound
Recorder also allows for the conversion of existing wave files. TAPI requires a call handle for
recording. For this reason, you cannot use the TApdTapiDevice for recording messages
unless you call your modem from another phone.
Recording options include the ability to detect silence on the line and take action when
silence is detected (such as hanging up the call). This is desirable because TAPI does not
have the ability to detect a hangup for a voice call. This option allows you to save disk space
by saving only the portion of the call which contains data.
Dual Tone Multiple Frequency (DTMF)
11
12
13
Dual Tone Multiple Frequency (DTMF) tones are generated by a telephone touch pad over
telephone lines. With compatible drivers and modems, Async Professional can detect
(receive) and generate these tones. Async Professional notifies an application when it
receives a tone by generating an OnTapiDTMF event. Tones are generated using the
SendTone method. See “TAPI Voice Support” in the Developer’s Guide for information
about supported operating systems.
14
15
16
17
406 Chapter 12: TAPI Components
1
1
TApdTapiDevice Component
1
TApdTapiDevice provides modem dialing, answering and configuration services using
Windows built-in TAPI support.
2
Hierarchy
3
TComponent (VCL)
4
! TApdBaseComponent (OOMisc) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
TApdCustomTapiDevice (AdTapi)
TApdTapiDevice (AdTapi)
6
Properties
AnswerOnRing
EnableVoice
ShowTapiDevices
ApiVersion
FailureCode
StatusDisplay
Attempt
FilterUnsupportedDevice
TapiLog
AvgWaveInAmplitude
FilterUnsupportedDevices
TapiState
BPSRate
InterruptWave
TrimSeconds
CallerID
MaxAttempts
SilenceThreshold
CallerIDName
MaxMessageLength
ComPort
Number
Cancelled
RetryWait
WaveFileName
DeviceCount
SelectedDevice
WaveState
Dialing
ShowPorts
UseSoundCard
7
8
9
10
! Version
11
12
13
14
15
16
17
TApdTapiDevice Component 407
1
1
1
2
3
4
5
6
7
Methods
AutoAnswer
GetDevConfig
ShowConfigDialog
AutomatedVoiceToComms
PlayWaveFile
ShowConfigDialogEdit
CancelCall
SaveWaveFile
StartWaveRecord
ConfigAndOpen
SelectDevice
StopWaveFile
CopyCallInfo
SendTone
StopWaveRecord
Dial
SetDevConfig
TapiStatusMsg
FailureCodeMsg
SetRecordingParams
TranslateAddress
OnTapiCallerID
OnTapiLog
OnTapiWaveNotify
OnTapiConnect
OnTapiPortClose
OnTapiWaveSilence
OnTapiDTMF
OnTapiPortOpen
OnTapiFail
OnTapiStatus
Events
8
9
10
11
12
13
14
15
16
17
408 Chapter 12: TAPI Components
1
1
Reference Section
1
AnswerOnRing
property
2
property AnswerOnRing : Byte
!
Default: 2
3
The number of times the TAPI device should allow the incoming call to ring before
answering it.
4
The default for AnswerOnRing is two rings because problems can occur with caller-ID
enabled modems if the call is answered before the first ring.
ApiVersion
read-only, run-time property
6
property ApiVersion : LongInt
! Returns the negotiated TAPI version level.
7
When an application initializes TAPI, it negotiates for a supported version of TAPI. Features
and behaviors differ among the versions. An application requests the highest level of TAPI
for which it was designed. The built-in TAPI services, even if they support a higher release
level, will behave as the requested version behaves.
The TApdTapiDevice always attempts to negotiate for version 1.4, but can use 1.3 if that is all
that is available. With operating systems supporting TAPI 2.0 or greater, TApdTapiDevice
will continue to operate at the 1.4 release level. See the README.TXT file for TAPI version
information.
Attempt
read-only, run-time property
property Attempt : Word
!
8
9
10
11
12
Indicates the number of times the current number has been dialed.
If the dialed number is busy, TAPI waits briefly and calls the number again. It tries up to
MaxAttempts times. The Attempt property returns the number of the current attempt.
Attempt is incremented immediately upon encountering a busy line. Attempt is primarily
for use in OnTapiStatus event handlers.
13
See also: MaxAttempts, OnTapiStatus, RetryWait
15
14
16
17
TApdTapiDevice Component 409
1
1
1
AvgWaveInAmplitude
read-only, run-time property
property AvgWaveInAmplitude : Integer
2
3
4
5
6
7
8
9
! Indicates the average relative amplitude of a recorded wave sample.
When a wave file is being recorded the AvgWaveInAmplitude property is updated to reflect
the average wave input amplitude of the recording. This value can be used to determine
spikes in the wave amplitude, which could mean the remote phone’s ringback, someone
saying “hello”, or it could just be an indicator of the speaker’s volume. When loud noises are
recorded AvgWaveInAmplitude will be larger than when silence is recorded. This property
is updated approximately every second while recording.
This property can also be used to determine a usable value for the SilenceThreshold
property. SilenceThreshold is the amplitude of recorded wave data to consider silence, and
can vary quite a bit between phone lines and modems. To attain a usable SilenceThreshold
value, record a few seconds of silence on the line. The AvgWaveInAmplitude property will
contain the value for SilenceThreshold for this modem/line combination.
This property is valid while a wave file is being recorded, which is after the StartWaveRecord
method has been called, up to the termination of the recording.
See also: OnTapiWaveSilence, SilenceThreshold, StartWaveRecord, StopWaveRecord,
TrimSeconds
AutoAnswer
10
11
12
13
procedure AutoAnswer;
! Instructs TAPI to listen for and answer incoming calls.
AutoAnswer returns immediately after instructing TAPI to listen for calls. TAPI listens for
calls in the background. When an incoming call is detected, it answers the call, generating
appropriate events as it does.
See “Answering calls” on page 402 for more information and an example.
14
15
16
17
410 Chapter 12: TAPI Components
1
1
method
AutomatedVoiceToComms
method
1
procedure AutomatedVoiceToComms;
! AutomatedVoiceToComms changes the TAPI media mode from voice to data
2
communications.
Call AutomatedVoiceToComms when switching from an automated voice mode (DTMF
tone detection, wave recording/playing) to a fax send/receive mode.
3
The OnTapiPortOpen event will be generated once TAPI successfully switches media modes
(from AutomatedVoice to DataModem). Note that Windows 2000 does not allow switching
media modes on an active call, so this method will fail under this operating system.
4
See also: OnTapiPortOpen
BPSRate
read-only, run-time property
property BPSRate : DWORD
6
7
! The rate of the current call in bits per second.
BPSRate is the rate negotiated between the local and remote modems for the current call. If a
call is not in progress, BPSRate returns zero.
8
The following example shows an OnTapiConnect event handler that updates a TLabel on the
current form with the negotiated bps rate:
9
TForm1 = class(TForm)
...
ApdTapiDevice1 : TApdTapiDevice;
Connect
: TLabel;
...
end;
10
11
procedure TForm1.ApdTapiDevice1TapiConnect(Sender : TObject);
begin
Connect.Caption :=
'Connected at ' + IntToStr(ApdTapiDevice1.BPSRate);
...
end;
12
13
14
15
16
17
TApdTapiDevice Component 411
1
1
1
CallerID
read-only, run-time property
property CallerID : string
2
3
! Contains the caller identification string of the current incoming call.
Many telephony environments make a caller identification string available. This string
usually contains the phone number of the incoming call, but can contain other information
as well, if supplemented by an office telephony system.
4
If the telephony environment doesn’t supply caller identification information, CallerID is an
empty string.
5
Note: Caller ID requires voice capabilities (i.e., UnimodmV, voice modem).
6
The following example shows an OnTapiConnect event handler that updates a TLabel on the
current form with the caller ID information:
7
8
9
10
11
TForm1 = class(TForm)
...
ApdTapiDevice1 : TApdTapiDevice;
CallerID
: TLabel;
...
end;
procedure TForm1.ApdTapiDevice1TapiConnect(Sender : TObject);
begin
CallerID.Caption := 'Caller: ' + ApdTapiDevice1.CallerID;
...
end;
CallerIDName
read-only, run-time property
property CallerIDName : string
12
! Contains the caller identification name information, if available.
13
The name information is not available everywhere that caller ID is available. Some localities
only provide the caller ID phone number.
14
See also: CallerID
15
16
17
412 Chapter 12: TAPI Components
1
1
CancelCall
method
1
procedure CancelCall;
! Disconnects the current call.
2
CancelCall is the TApdTapiDevice universal method for terminating the current call. It can
be used while waiting for incoming call, answering an incoming call, dialing a call, or during
an established connection. TAPI aborts the current process, assures that the modems have
disconnected, and places the modem into an idle state (not waiting for calls).
CancelCall is usually a background operation. It instructs TAPI to cancel the call and TAPI
performs the work of canceling and cleaning up. OnTapiPortclose will fire when TAPI closes
the port (assuming the port was open). OnTapiFail will always be generated when
CancelCall completes. To determine whether the OnTapiFail was generated due to
CancelCall, use the Cancelled property.
3
4
6
See also: Cancelled, OnTapiFail, OnTapiPortClose
Cancelled
run-time, read-only property
7
8
property Cancelled : Boolean
! Returns True if an OnTapiFail event fires due to user action.
An OnTapiFail event is generated any time a call is terminated before the final connection is
made—even if CancelCall is used to terminate the call. If CancelCall was used to terminate
the connection, Cancelled will be True.
9
10
See also: CancelCall, OnTapiFail
ComPort
property
property ComPort : TApdCustomComPort
11
12
! Determines the TApdComPort component used by the TApdTapiDevice.
A properly initialized TApdComPort must be assigned to this property before dialing or
answering calls.
13
ComPort is usually set automatically at design time to the first TApdComPort component
the TApdTapiDevice finds on the form. If needed, use the Object Inspector to select a
different TApdComPort component.
14
Setting the ComPort property at run time is necessary only when using a dynamically
created TApdComPort or when selecting among several TApdComPort components.
15
16
17
TApdTapiDevice Component 413
1
1
1
ConfigAndOpen
method
procedure ConfigAndOpen;
2
3
4
! Configures the modem and leaves the port open in passthrough mode.
ConfigAndOpen takes advantage of the TAPI modem configuration facilities, even though
TAPI isn’t used for dialing or answering a call. ConfigAndOpen does, however, require a
short period of background processing before the associated TApdComPort component is
open.
You could not, for example, use the following logic:
5
6
7
8
9
10
11
12
13
14
15
16
var
ApdComPort1
: TApdComPort;
ApdTapiDevice : TApdTapiDevice;
...
ApdTapiDevice1.ConfigAndOpen;
ApdComPort1.Output := 'ready';
...more port I/O
ApdComPort1.Open := False;
This is incorrect. After the call to ConfigAndOpen you must wait for TAPI to open the
TApdComPort, just as when TAPI is dialing or answering a call. When TAPI is ready,
(usually just a second or two) it generates the OnTapiPortOpen event which automatically
opens the associated TApdComPort.
The following example shows the proper way to use ConfigAndOpen:
procedure TForm1.OpenTheLine;
begin
ApdTapiDevice1.ConfigAndOpen;
end;
...
procedure TForm1.ApdTapiDevice1TapiPortOpen(Sender : TObject);
begin
ApdComPort1.Output := 'ready';
...more port I/O
end;
...
procedure TForm1.CloseTheLine;
begin
ApdTapiDevice1.CancelCall;
end;
See “Using TAPI for configuration only” on page 405 for more information.
17
414 Chapter 12: TAPI Components
1
1
CopyCallInfo
method
1
procedure CopyCallInfo(var CallInfo : PCallInfo);
! Returns a record containing details about the current call.
2
The TApdTapiDevice contains properties for the basic call information that a typical
“dialer” program needs about the current call. TAPI, however, maintains much more
information about the call. Although this information is of limited use, CopyCallInfo
provides it for applications that might need it.
3
4
CallInfo is a pointer to a PCallInfo structure, which is allocated by CopyCallInfo and filled
in by TAPI. The calling program is responsible for deallocated CallInfo when it is no longer
needed. The size of the allocated structure varies depending on the information TAPI has
about the current call.
The fields in the TCallInfo record are not described here. See page 393 for a list of TAPI
references.
DeviceCount
read-only, run-time property
6
7
8
property DeviceCount : LongInt
! The number of currently installed TAPI devices.
DeviceCount is the number of TAPI devices installed on the machine and available for use
by TAPI applications. Typically, this is the same as the number of modems installed on the
machine (usually one).
9
10
See also: FilterUnsupportedDevices, SelectedDevice
Dial
method
procedure Dial(ANumber : string);
11
12
! Dials a phone number in the background.
Dial instructs TAPI to prepare the modem for dialing, then to dial ANumber. All of these
operations take place in the background. TApdTapiDevice generates the OnTapiStatus event
to keep the program apprised of the dialing progress.
If EnableVoice is False (you are attempting a data connection), the OnTapiPortOpen event
should be used as your notification that a data connection is established. If EnableVoice is
True (you are attempting a voice connection), the OnTapiConnect event should be used as
your notification. The OnTapiConnect event may be generated for data connections (TSP
dependent), but this event does not indicate that the TApdComPort has a valid, open serial
port.
13
14
15
16
17
TApdTapiDevice Component 415
1
1
1
2
If a busy signal is detected and MaxAttempts is greater than one, Dial redials the number
after waiting RetryWait seconds. This continues until a connection is established or
MaxAttempts dial attempts fail.
6
Due to a limitation of the Microsoft supplied TAPI Service Providers, the OnTapiConnect
event is not completely reliable for detecting when the called party answers a voice call.
Unimodem/V and Unimodem/5 do not implement the call progress notification techniques
required to detect when a voice connection is actually established. As a result of this, the
OnTapiConnect event is usually generated immediately after the modem completes dialing,
regardless of whether the remote party answered, their phone was busy, or any other
conditions. With some TSPs, it is also possible to get an OnTapiConnect event followed by
an OnTapiFail event, if the call was busy or no dial tone was detected. The APROFAQ.HLP
file contains several tips and tricks to detect when the remote party actually answers. TSPs
supplied with dedicated voice boards usually provide much more detailed and accurate call
progress notification.
7
The following example shows how to dial the U.S. Robotics BBS, waiting 5 minutes after a
busy signal and retrying up to 10 times:
3
4
5
8
9
ApdTapiDevice1.RetryWait := 300;
ApdTapiDevice1.MaxAttempts := 10;
ApdTapiDevice1.Dial('1-847-262-6000');
See also: AutoAnswer, MaxAttempts, Number, RetryWait
Dialing
10
11
12
property Dialing : Boolean
! Determines whether TAPI is placing an outgoing call or listening for an incoming call.
Dialing is True when TAPI is placing an outgoing call, False when TAPI is listening for or
answering incoming calls. Dialing is intended primarily for use in status routines to
distinguish between status events for incoming calls and status events for outgoing calls.
13
14
15
16
17
416 Chapter 12: TAPI Components
1
1
read-only, run-time property
The following example shows an OnTapiStatus event handler that uses Dialing to update a
TLabel on the current form:
TForm1 = class(TForm)
...
ApdTapiDevice1 : TApdTapiDevice;
Direction
: TLabel;
...
end;
1
2
3
procedure TForm1.ApdTapiDevice1TapiStatus(
CP : TObject; First, Last : Boolean; Device, Message, Param1,
Param2, Param3 : LongInt);
const
DirectionStr : array[Boolean] of string = (
'Incoming', 'Outgoing');
begin
...
Direction.Caption := DirectionStr[ApdTapiDevice1.Dialing];
...
end;
4
6
7
8
See also: Number
EnableVoice
property
9
property EnableVoice : Boolean
10
Default: False
! Determines whether the initial mode of calls is DataModem (Fax or Data) or
AutomatedVoice (Voice/DTMF).
If EnableVoice is True and a TAPI device is selected, Async Professional first verifies that
AutomatedVoice capabilities exist for the selected device. If so, voice extensions such as
DTMF and wave files are supported. Otherwise an ETapiVoiceNotSupported exception is
raised and EnableVoice is set to False.
11
12
13
See also: OnTapiDTMF, OnTapiWave, PlayWaveFile, SendTone
14
15
16
17
TApdTapiDevice Component 417
1
1
1
FailureCode
read-only, run-time property
property FailureCode : Integer
2
3
4
5
6
7
8
9
10
11
12
! FailureCode indicates the last TAPI failure.
During a Dial or AutoAnswer attempt, TAPI could detect a failure and generate the
OnTapiFail event. The FailureCode property indicates the most severe error reported by
TAPI. The value of FailureCode will be one of the LineCallState_ or LineDisconnectMode_
constants defined in AdTUtil.pas.
The FailureCodeMsg function will convert a FailureCode into a descriptive string based on
the string resources.
The following example determines the reason for the failure and displays a corrective action
to the user:
uses
AdTUtil; { for the error constants }
procedure TForm1.ApdTapiDevice1TapiFail(Sender: TObject);
begin
case ApdTapiDevice1.FailureCode of
LineDisconnectMode_Busy : ShowMessage(
'The number was busy, try again later');
LineDisconnectMode_NoAnswer : ShowMessage(
'No answer, try again later');
LineDisconnectMode_NoDialtone : ShowMessage(
'No dialtone, check your phone line');
else
ShowMessage(
ApdTapiDevice1.FailureCodeMsg(ApdTapiDevice1.FailureCode));
end;
end;
See also: FailureCodeMsg, OnTapiFail
13
14
15
16
FailureCodeMsg
function FailureCodeMsg(const FailureCode : Integer) : string;
! FailureCodeMsg converts a FailureCode into a descriptive string.
FailureCodeMsg will return a text message describing the failure code. FailureCode is the
failure code, usually provided by the FailureCode property. See FailureCode for an example
of how to use this method.
See also: FailureCode, OnTapiFail, TapiStatusMsg
17
418 Chapter 12: TAPI Components
1
1
method
FilterUnsupportedDevices
property
1
property FilterUnsupportedDevices : Boolean
2
Default: True
! Determines whether unsupported devices are displayed in the TAPI device selection dialog
3
box.
The TApdTapiDevice support TAPI line devices, with the DataModem and AutomatedVoice
media modes. Some TAPI devices, notably the IP telephony devices in Windows 2000, are
not supported by the TApdTapiDevice. If this property is True (the default) these devices
will not be displayed in the TAPI device selection dialog. If this property is False, these
devices will be displayed in the dialog.
See also: SelectDevice
GetDevConfig
4
6
method
7
function GetDevConfig : TTapiConfigRec;
! Returns the configuration of the currently selected device.
The Data is binary, and described in the TAPI documentation as an “opaque” structure,
meaning that it is not guaranteed to be consistent across different TAPI devices. For this
reason, GetDevConfig must be called for a given device at some point before calling
SetDevConfig or ShowConfigDialogEdit.
The record used by GetDevConfig (and the following configuration methods) is defined as
follows:
8
9
10
11
TTapiConfigRec = record
DataSize : Cardinal;
Data : array[0..1023] of Byte;
end;
12
See also: SetDevConfig, ShowConfigDialogEdit
13
14
15
16
17
TApdTapiDevice Component 419
1
1
1
InterruptWave
run-time property
property InterruptWave : Boolean
2
Default: True
! Indicates whether the current wave file should stop when a DTMF tone is detected.
3
4
5
6
7
8
9
10
If InterruptWave is True, the currently playing wave file will stop when a DTMF tone is
detected. This allows you to have an automated phone answering system in which user is
allowed to interrupt the currently playing wave file with a DTMF selection. If InterruptWave
is False, DTMF tones do not stop the currently playing wave file. If you want the caller to
hear a wave file in its entirety, set InterruptWave to False before you start playing it.
See also: OnTapiDTMF, PlayWaveFile, StopWaveFile
MaxAttempts
property MaxAttempts : Word
Default: 3
! Determines the number of times Dial automatically dials a number.
This is the number of times a phone number is dialed, it is not the number of retries. When
MaxAttempts is one, for example, the number is dialed only once. If the line is busy, it is not
tried again.
See also: Attempt, RetryWait
MaxMessageLength
11
12
property
run-time property
property MaxMessageLength : LongInt
Default: 60
! The maximum allowed message length, in seconds, for messages recorded over the TAPI
13
waveform audio device.
14
Use this parameter to specify the maximum length of recorded messages. A 60-second
message will require about 950K of disk space given the default recording parameters. When
the specified length of time passes, the OnTapiWaveNotify event will be generated with a
Msg parameter of waDataReady. You could then save the wave file and terminate the call.
15
16
If the TrimSeconds property is set to a non-zero value then wave recording may terminate
before MaxMessageLength is reached.
17
420 Chapter 12: TAPI Components
1
1
Number
read-only, run-time property
1
property Number : string
! The last phone number dialed.
2
Number is intended primarily for use in status routines, to display the last number dialed.
Number is updated each time Dial is called.
3
The following example shows an OnTapiStatus event handler that displays the last number
dialed.
4
TForm1 = class(TForm)
...
ApdTapiDevice1 : TApdTapiDevice;
NumberDialed : TLabel;
...
end;
6
procedure TForm1.ApdTapiDevice1TapiStatus(
CP : TObject; First, Last : Boolean; Device, Message, Param1,
Param2, Param3 : LongInt);
begin
...
if Dialing then
NumberDialed.Caption := ApdTapiDevice1.Number
else
NumberDialed.Caption := '';
...
end;
...
ApdTapiDevice1.Dial('1-847-262-6000');
See also: Dial, Dialing
7
8
9
10
11
12
13
14
15
16
17
TApdTapiDevice Component 421
1
1
1
OnTapiCallerID
event
property OnTapiCallerID : TTapiCallerIDEvent
2
3
4
5
6
7
8
TTapiCallerIDEvent = procedure(
CP : TObject; ID, IDName : String) of object;
! Defines an event handler that is called after a connection is made and both a Caller ID string
and a Caller ID name string are returned.
The OnTapiCallerID event makes it easy to access Caller ID information without having to
know when it might be available on a call. Caller ID information is available only if it is
supported on the selected device and by the telephone service.
The following example shows how to use the OnTapiCallerID event to get the Caller ID
information and store it to edit controls.
procedure TForm1.ApdTapiDevice1TapiCallerID(
CP : TObject; ID, IDName : string);
begin
CallerId.Text := ID;
CallerIdName.Text := IDName;
end;
See also: CallerID, CallerIDName
9
10
OnTapiConnect
event
property OnTapiConnect : TNotifyEvent
! Defines an event handler that is called when a connection is established.
11
12
13
14
Dial and AutoAnswer operations take place in the background. If a connection is established
after a call to Dial or AutoAnswer, the TApdTapiDevice generates the OnTapiConnect event.
No parameters are passed to the OnTapiConnect event. The OnTapiConnect event is most
useful and reliable when used to indicate when a voice connection has been established. For
data connections (EnableVoice = False), the TApdComPort may not have received the serial
port handle from TAPI when this event is generated. When you are establishing data
connections, use the OnTapiPortOpen event instead.
15
16
17
422 Chapter 12: TAPI Components
1
1
OnTapiDTMF
event
1
property OnTapiDTMF : TTapiDTMFEvent
2
TTapiDTMFEvent = procedure(
CP : TObject; Digit : Char; ErrorCode : Longint;) of object;
! Defines an event handler that is called when a DTMF tone is detected.
3
Digit is a character that represents the phone button that was pressed on the remote phone
device. The possible values are ‘0’ through ‘9’, ‘*’, and ‘#’. ErrorCode is non-zero if an error
occurs when a TAPI connection is made (in this case the OnTapiDTMF is generated just
before the OnTapiConnect event).
4
The following example builds a string of up to ten DTMF tones (characters) in the global
variable S.
6
procedure TForm1.ApdTapiDevice1TapiDTMF(
CP : TObject; Digits : Char; ErrorCode : LongInt);
begin
if Length(S) < 11 then
S := S + Digit;
end;
7
8
See also: EnableVoice
OnTapiFail
event
9
10
property OnTapiFail : TNotifyEvent
! Defines an event handler that is called when a connection attempt fails.
Dial and AutoAnswer operations take place in the background. If an attempt to establish a
connection fails, the TApdTapiDevice generates the OnTapiFail event.
No parameters are passed to OnTapiFail. It is a notification to the application that a
connection attempt failed.
The FailureCode property will contain the most severe error reported by TAPI. See the
description of FailureCode for an example of how to use that property and the OnTapiFail
event handler.
11
12
13
14
See also: FailureCode, FailureCodeMsg
15
16
17
TApdTapiDevice Component 423
1
1
1
OnTapiLog
event
property OnTapiLog : TTapiLogEvent
2
TTapiLogEvent = procedure(
CP : TObject; Log : TTapiLogCode) of object;
3
! Defines an event handler that is called at designated points during a dial or answer attempt.
4
The primary purpose of this event is to give the application a chance to log auditing
information about telephone calls and whether they succeed or fail. You can also use this
event for start-up and cleanup activities, although OnTapiPortOpen and OnTapiPortClose
might be better.
5
6
CP is the TAPI component that generated the event. Log is a code that indicates the state of
the TAPI connection. The possible states are listed in “TAPI logging” on page 397. No other
information is passed with this event, but you can use the TApdTapiDevice properties such
as Number and Dialing to get additional information about the TAPI connection.
7
See “TAPI logging” on page 397 for more information.
See also: Dialing, Number, TapiLog
8
OnTapiPortClose
9
10
property OnTapiPortClose : TNotifyEvent
! Defines an event handler that is called immediately after TApdTapiDevice closes its
associated TApdComPort.
11
The TApdTapiDevice component is responsible for opening and closing the associated
TApdComPort at the appropriate times (when a connection is established or broken).
12
The serial port handle is invalid once the OnTapiPortClose event is generated, attempts to
access the port will raise the EPortNotOpen exception.
Applications can use this event to perform additional port cleanup activities.
13
See also: OnTapiPortOpen
14
15
16
17
424 Chapter 12: TAPI Components
1
1
event
OnTapiPortOpen
event
1
property OnTapiPortOpen : TNotifyEvent
! Defines an event handler that is called immediately after TApdTapiDevice opens its
2
associated TApdComPort.
The TApdTapiDevice component is responsible for opening and closing the associated
TApdComPort at the appropriate times (when a connection is established or broken).
3
The serial port associated with the selected TAPI device is valid, and available when this
event is generated, it is safe to access the port properties and transmit characters.
4
Note that this event is not generated during a voice connection. When a voice connection is
made TAPI retains exclusive access to the serial port. This event will be generated after a call
to AutomatedVoiceToComms, once TAPI hands the TApdTapiDevice a valid serial port
handle.
Applications can use this event to perform additional port setup activities.
7
See also: OnTapiPortClose
OnTapiStatus
6
event
property OnTapiStatus : TTapiStatusEvent
TTapiStatusEvent = procedure(CP : TObject; First, Last : Boolean;
Device, Message, Param1,Param2, Param3 : Cardinal) of object;
! Defines an event handler that is called regularly during a TAPI dial or answer attempt.
TAPI performs dial and answer activities in the background, calling a callback routine
whenever the state of the line or call changes. TApdTapiDevice installs a hidden callback
routine and translates all callback calls into OnTapiStatus events.
8
9
10
11
12
CP is the TApdTapiDevice component that generated the event.
First is True on the first OnTapiStatus event to signal the status routine to perform its startup activities (e.g., make the status display visible). Last is True on the last OnTapiStatus
event to signal the status routine to perform its cleanup activities (e.g., remove the status
display). First and Last are False on all other OnTapiStatus events.
The other parameters are the ones passed by TAPI to the callback routine. The only
parameters that are of interest to most Async Professional programs are Message and
Param1, which indicate the state of the current call. See “TAPI status processing” on page
394 for more information about the values of Message and Param1.
13
14
15
16
17
TApdTapiDevice Component 425
1
1
1
2
3
4
The remaining parameters (Device, Param2, and Param3) are intended for use in
applications that extend the features provided by TApdTapiDevice and might need those
status parameters.
TAPI generates callbacks only when it perceives a change in the state of the line or call. TAPI,
therefore, does not generate callbacks when the modem stays in a single state for an
extended period of time. For example, after dialing a number TAPI reports that the call is in
the “proceeding” phase. It generates no further status callbacks until the call succeeds or
fails. Since this can take many seconds, as much as 20 or even 30 seconds, the user might
become concerned about the lack of positive feedback (is it still working?).
5
To solve this problem, TApdTapiDevice generates additional OnTapiStatus events, based on
an internal timer (once per second). These status calls give the status routine an opportunity
to update a timer, as the built-in TApdTapiStatus does.
6
OnTapiWaveNotify
7
property OnTapiWaveNotify : TTapiWaveNotify
8
event
TTapiWaveEvent = procedure(
CP : TObject; Msg : TWaveMessage) of object;
TWaveMessage = (waPlayOpen, waPlayDone, waPlayClose,
waRecordOpen, waDataReady, waRecordClose);
9
10
! Defines an event handler that is called when a wave file status changes.
The possible values for Msg are:
Value
Meaning
11
waPlayDone
The wave file is finished playing (it either completed
normally or was stopped by a call to StopWaveFile).
12
waPlayOpen
The wave file is open.
waPlayClose
The wave file is closed.
waRecordOpen
The wave device is open for recording.
waRecordClose
The wave device is closed.
waDataReady
The wave device has recorded data and is ready to be
saved.
13
14
15
16
17
426 Chapter 12: TAPI Components
1
1
The following example sets the Caption of a label after a wave file has finished playing:
1
procedure TForm1.ApdTapiDevice1TapiWaveEvent(
CP : TObject; Msg : TWaveMessage);
begin
if Msg = waPlayDone then
Label4.Caption := 'Wave Device Idle...';
end;
2
3
See also: PlayWaveFile, StartWaveRecord, StopWaveFile, StopWaveRecord
4
OnTapiWaveSilence
event
FOnTapiWaveSilence : TTapiWaveSilence
TTapiWaveSilence = procedure(CP : TObject;
var StopRecording : Boolean; var Hangup : Boolean) of object;
6
! Defines an event handler that is called when silence is detected while recording a wave file.
StopRecording is a var Boolean parameter that determines whether wave recording should
stop. This parameter is True by default. Hangup is a var Boolean parameter that determines
whether the call should be terminated. The parameter is also True by default.
This event works in conjunction with the TrimSeconds property. If TrimSeconds is 0 then
OnTapiWaveSilence will not be generated. If you do not respond to this event then recording
will stop and the call will be terminated when silence is detected. This is probably the
desired behavior in most applications, so you may not use this event very often.
See also: StartWaveRecord, StopWaveRecord, TrimSeconds
PlayWaveFile
method
procedure PlayWaveFile(FileName : String);
7
8
9
10
11
12
! Plays a wave file.
FileName is the name of the wave file. The wave file starts playing immediately if there is not
a wave file currently playing. If another wave file is currently playing and InterruptWave is
True, the current wave file is stopped and the new wave file is played. If another wave file is
playing and InterruptWave is False, PlayWaveFile returns without playing the new wave file.
The wave file is played through the TAPI device if the UseSoundCard property is False (the
default), or through the sound card if UseSoundCard is True.
13
14
15
The following example plays a wave file through the TAPI device:
16
ApdTapiDevice1.PlayWaveFile('greeting.wav');
See also: InterruptWave, OnTapiWaveNotify, StopWaveFile, UseSoundCard
17
TApdTapiDevice Component 427
1
1
1
RetryWait
property
property RetryWait : Word
2
Default: 60
! The number of seconds to wait after a busy signal before trying the number again.
3
4
5
After encountering a busy signal, TApdTapiDevice checks to see if it should try this number
again by comparing Attempts to MaxAttempts. If more attempts are required, it first waits
RetryWait seconds before dialing again to give the dialed machine time to complete the
current session.
See also: Attempts, MaxAttempts
SaveWaveFile
method
6
procedure SaveWaveFile(FileName : String; Overwrite : Boolean);
7
! Saves recorded wave data to disk.
9
FileName is the file to save. Overwrite indicates whether an existing file should be
overwritten. If Overwrite is False and a file with the same name exists, then an exception is
thrown. By default, data is recorded at 8,000 kHz, 16 bits, mono (the maximum sound
quality allowed for most TAPI wave devices). You can save the wave data after the
OnTapiWaveNotify event is received with a Msg parameter of waDataReady.
10
The following example starts recording wave data on a button click and saves the recorded
data when notified that the recording is done:
8
11
12
13
14
15
procedure TForm1.Button1Click(Sender : TObject);
begin
ApdTapiDevice1.StartWaveRecord;
end;
procedure TForm1.ApdTapiDevice1TapiWaveNotify(
CP : TObject; Msg : TWaveMessage);
begin
if Msg = waDataReady then
ApdTapiDevice1.SaveWaveFile('Call01.wav', True);
end;
See also: OnTapiWaveNotify, StartWaveRecord, StopWaveRecord
16
17
428 Chapter 12: TAPI Components
1
1
SelectDevice
method
1
procedure SelectDevice;
! Displays a dialog box to select a TAPI device.
2
SelectDevice uses the same dialog box as the property editor for SelectedDevice. You can
call SelectDevice to prompt the user for a TAPI device to use for subsequent dial or answer
operations.
3
See SelectedDevice for the displayed dialog box.
4
See also: FilterUnsupportedDevices, ShowTapiDevices
SelectedDevice
property
6
property SelectedDevice : string
! Determines the TAPI device to be used for dialing and answering.
TAPI assigns names to each installed modem. TApdTapiDevice components select among
those devices by setting SelectedDevice to the name of the desired TAPI device. Since these
names sometimes be rather lengthy and cumbersome to type, a property editor is provided
for easier selection of devices.
Because the name specified in SelectedDevice must exactly match a TAPI device name, you
should use this component in your application if you need to allow users to select a TAPI
device.
SelectedDevice must be set before calling Dial, AutoAnswer, or ShowConfigDialog or they
will raise an ETapiNoSelect exception.
SendTone
7
8
9
10
11
method
12
procedure SendTone(Digits : string);
! Sends a DTMF tone to a remote telephone.
SendTone replicates the press of a telephone touch pad button from within an application.
Digits should consist of valid telephone touch pad buttons (i.e., ‘1’ through ‘9’, ‘*’, and ‘#’).
You can also use a comma (,) between characters for a short delay between the tones.
Multiple comma characters can be used to create a longer delay.
The following example demonstrates how to use SendTone to send multiple tones with a
delay.
SendTone('123456789,,0');
See also: EnableVoice, OnTapiDTMF
13
14
15
16
17
TApdTapiDevice Component 429
1
1
1
SetDevConfig
method
procedure SetDevConfig(const Config : TTapiConfigRec);
2
3
! Sets the selected device to the configuration defined in Config.
The selected device does not have to be “open” at the time of configuration. Config must
originate from a previous call to GetDevConfig or ShowConfigDialogEdit.
See also: GetDevConfig, ShowConfigDialogEdit
4
5
6
7
8
9
10
11
SetRecordingParams
procedure SetRecordingParams(NumChannels : Byte;
NumSamplesPerSecond : Integer; NumBitsPerSample : Byte);
! Sets the parameters used to record a wave file.
NumChannels is the number of channels to use for recording. A value of 1 indicates mono,
and a value of 2 indicates stereo. Due to the nature of telephony, it is unlikely any TAPI
devices support stereo recording. NumSamplesPerSecond is the number of samples per
second to use for recording. NumBitsPerSample is the number of bits of data to record per
sample.
By default recording parameters are set to 1 channel (mono), 8000 samples per second, 16
bits per sample. If your TAPI device supports other recording formats you can use this
method to change the recording format. If you set the recording parameters to values not
supported by your TAPI device you will get an ETapiWaveError when you attempt to begin
recording. It is unlikely that you will need to change the recording parameters.
See also: StartWaveRecord
ShowConfigDialog
12
13
14
! Displays the TAPI property sheets for the selected TAPI device.
TAPI maintains a set of port properties for each TAPI device. These property sheets are
accessible from the Windows Modem applet in the Control Panel. You can also make them
available in your application by calling ShowConfigDialog.
16
17
430 Chapter 12: TAPI Components
1
method
procedure ShowConfigDialog;
15
1
method
ShowConfigDialogEdit
method
function ShowConfigDialogEdit(
const Init : TTapiConfigRec) : TTapiConfigRec;
1
2
! Shows a TAPI configuration dialog for the selected device.
Init must be initialized with a TTapiConfigRec obtained from a call to GetDevConfig with
the same selected device. The returned TTapiConfigRec reflects any changes the user made
to the configuration. The device itself is not updated at this point—the record is merely
returned for use in a subsequent call to SetDevConfig.
3
4
See also: GetDevConfig, SetDevConfig
ShowPorts
property
6
property ShowPorts : Boolean
Default: True
! Controls whether serial ports are displayed by the SelectDevice method.
7
ShowPorts is used in conjunction with ShowTapiDevices to determine what is displayed in
the Device Selection dialog. The dialog is used as a property editor for the SelectedDevice
property at design time, when you call the SelectDevice method at run time, and when a
TAPI command is executed before a TAPI device is selected. See SelectedDevice to see a
sample Device Selection dialog box.
8
If ShowPorts is True, the available serial ports are displayed in the drop-down box in the
Device Selection dialog box. If ShowTapiDevices is True, the TAPI devices are displayed. If
both ShowPorts and ShowTapiDevices are False, nothing is shown in the drop-down box in
the dialog.
10
When ShowPorts is True, the available serial ports will be displayed in the dialog box. If one
of these devices is selected, the TApdComPort.TapiMode will be set to tmOff. Use TapiMode
to determine whether to open the port with ConfigAndOpen or whether to open the
TApdComPort directly.
12
9
11
13
See also: SelectedDevice, ShowTapiDevices
14
15
16
17
TApdTapiDevice Component 431
1
1
1
ShowTapiDevices
property
property ShowTapiDevices : Boolean
2
Default: True
! Controls whether TAPI devices are displayed by the SelectDevice method.
3
4
5
If ShowTapiDevices is True, SelectDevice shows both TAPI devices and available serial
ports. If ShowTapiDevices is False, SelectDevice shows only serial ports.
See also: SelectDevice, ShowPorts
SilenceThreshold
run-time property
property SilenceThreshold : Integer
6
7
8
9
10
11
12
13
14
15
16
Default: 50
! Specifies a value that is used as a measure of silence.
When the TrimSeconds property is set to a non-zero value, the wave data is examined as it is
recorded. Silence is determined by comparing the average of the wave data for one second to
a silence threshold as defined by SilenceThreshold. PCM data recorded by the TAPI wave
driver generally has an amplitude of 400 to 800 for normal speech. A silence threshold of 50
(the default) is conservative. True silence on the phone line is probably less than 20,
although anything under 200 could probably be considered silence. Modify the
SilenceThreshold property if your phone lines contain more or less noise.
See also: OnTapiWaveSilence, StartWaveRecord, StopWaveRecord, TrimSeconds
StartWaveRecord
procedure StartWaveRecord;
! Starts the wave device recording.
Use StartWaveRecord to begin recording a wave file using the TAPI waveform audio device.
Recording stops when the StopWaveRecord method is called, when the wave input buffer is
full, or when silence is detected on the line. The size (in seconds) of the wave input buffer is
determined by MaxMessageLength. Silence detection is controlled through the
TrimSeconds property.
When recording stops, the OnTapiWaveNotify event is generated with Msg set to
waDataReady. After receiving notification that wave data is ready, you must save the
recorded data using SaveWaveFile.
17
432 Chapter 12: TAPI Components
1
1
method
The following example sets the maximum message length to 45 seconds, starts recording
wave data on a button click, and then saves the recorded data when notified that the
recording is done:
1
procedure TForm1.Button1Click(Sender : TObject);
begin
ApdTapiDevice1.MaxMessageLength := 45;
ApdTapiDevice1.StartWaveRecord;
end;
2
procedure TForm1.ApdTapiDevice1TapiWaveNotify(
CP : TObject; Msg : TWaveMessage);
begin
if Msg = waDataReady then
ApdTapiDevice1.SaveWaveFile('Call01.wav');
end;
4
3
6
See also: MaxMessageLength, PlayWaveFile, SaveWaveFile, StopWaveRecord, TrimSeconds
StatusDisplay
property
property StatusDisplay : TApdAbstractTapiStatus
7
8
! An instance of a TAPI status window.
If StatusDisplay is nil (the default), TApdTapiDevice does not provide an automatic status
window. You can install an OnTapiStatus event handler to display status in this case.
If you create an instance of a class derived from TApdAbstractTapiStatus or use the supplied
TApdTapiStatus component (see page 443) and assign it to StatusDisplay, the status window
is displayed and updated automatically.
StatusDisplay is usually set automatically at design time to the first TApdAbstractStatus or
derived component the TApdTapiDevice finds on the form. If necessary, use the Object
Inspector to select a different status component.
Setting the StatusDisplay property at run time is necessary only when using a dynamically
created status display or when selecting among several status components.
9
10
11
12
13
14
15
16
17
TApdTapiDevice Component 433
1
1
1
StopWaveFile
method
procedure StopWaveFile;
2
3
4
! Stops the wave file that is currently playing.
The wave file is halted regardless of the value of the InterruptWave property. StopWaveFile
generates two OnTapiWave events. The first has a Code of WOM_DONE and the second has
a Code of WOM_CLOSE. If no wave file is currently playing then StopWaveFile returns
silently. The following example stops a wave file, if one is currently playing:
if ApdTapiDevice1.WaveState = wsPlaying then
ApdTapiDevice1.StopWaveFile;
5
6
See also: InterruptWave, OnTapiWaveNotify, PlayWaveFile, WaveStatus
StopWaveRecord
method
procedure StopWaveRecord;
7
8
9
10
11
! Stops the wave file that is recording.
It is not always necessary to call this function since wave recording may be halted as the
result of the wave buffer filling up, or as a result of silence on the line. The wave record buffer
is set to an initial size based on MaxMessageLength. If you do not call StopWaveRecord, the
recording automatically stops after MaxMessageLength seconds. The TrimSeconds property
is set to 2 seconds by default. Wave recording will stop after 2 seconds of silence are detected
on the line (unless you respond to the OnTapiWaveSilence event and override the default).
See also: MaxMessageLength, OnTapiWaveNotify, SaveWaveFile, StartWaveRecording,
TrimSeconds
TapiLog
12
13
14
property TapiLog : TApdTapiLog
! An instance of a TAPI logging component.
If TapiLog is nil (the default), TApdTapiDevice does not provide automatic logging. You can
install an OnTapiLog event handler to provide logging services in this case.
15
TapiLog is usually set automatically at design time to the first TApdTapiLog or derived
component the TApdTapiDevice finds on the form. If necessary, use the Object Inspector to
select a different a logging component.
16
Setting the TapiLog property at run time is necessary only when using a dynamically created
logging component or when selecting among several logging components.
17
434 Chapter 12: TAPI Components
1
1
property
TapiState
read-only, run-time property
1
property TapiState : TTapiState
TTapiState = (tsIdle, tsOffering, tsAccepted, tsDialTone,
tsDialing, tsRingback, tsBusy, tsSpecialInfo, tsConnected,
tsProceeding, tsOnHold, tsConferenced, tsOnHoldPendConf,
tsOnHoldPendTransfer, tsDisconnected, tsUnknown);
2
3
Default: tsNone
4
! The state of the TAPI operation.
When TapiState is referenced, APRO retrieves state information from TAPI and returns
the result as TapiState. For completeness, the TTapiState enumeration contains all
possible returns from TAPI -- which is a superset of values that you will likely see in an
APRO application.
Note: Since APRO retrieves this value from TAPI every time you check it, you should avoid
calling it too often. In other words, sitting in a loop continuously polling TapiState would
not be a good idea.
6
7
8
Value
Meaning
tsIdle
No TAPI operations in progress.
tsOffering
TAPI is offering an incoming call.
tsAccepted
APRO has accepted an incoming call.
tsDialTone
Dial tone detected.
tsDialing
Waiting for dial to complete or fail.
tsRingback
Ringback detected.
tsBusy
Line is busy.
tsSpecialInfo
TAPI service provider specific.
tsConnected
Call is connected.
tsProceeding
Call is proceeding.
tsOnHold
Call has been placed on hold.
tsConferenced
Call is conferenced.
9
10
11
12
13
14
15
16
17
TApdTapiDevice Component 435
1
1
1
2
3
Value
Meaning
tsOnHoldPendConf
Call is being conferenced.
tsOnHoldPendTransfer
Call is being transferred.
tsDisconnected
Call has been disconnected.
tsUnknown
TAPI state unknown to APRO.
TapiStatusMsg
method
4
function TApdCustomTapiDevice.TapiStatusMsg(
const Message, State, Reason : DWORD) : string;
5
6
7
8
9
10
11
12
13
14
15
! Returns a text message for the progress of the current TAPI call.
TapiStatusMsg is intended primarily for use in OnTapiStatus event handlers. Message is the
Message parameter passed into OnTapiStatus. State is the Param1 parameter passed into
OnTapiStatus. TapiStatusMsg combines the values of Message and State to arrive at a unique
resource string ID, stored in APW.RC. Reason is the Param2 parameter passed into
OnTapiStatus. Reason adds additional information for LineCallState_Disconnected
messages. This parameter is ignored for all other messages.
The following example shows an OnTapiStatus event handler that calls TapiStatusMsg and
displays the returned string:
TForm1 = class(TForm)
...
ApdTapiDevice1 : TApdTapiDevice;
StatusStr
: TLabel;
...
end;
procedure TForm1.ApdTapiDevice1TapiStatus(
CP : TObject; First, Last: Boolean; Device, Message, Param1,
Param2, Param3 : LongInt);
begin
...
StatusStr.Caption :=
ApdTapiDevice1.TapiStatusMsg(Message, Param1, Param2);
...
end;
See also: OnTapiStatus
16
17
436 Chapter 12: TAPI Components
1
1
TranslateAddress
method
1
function TranslateAddress(CanonicalAddr : String) : String;
! Translates a canonical address into a dialable address.
2
A canonical address is an address that contains the country code as well as the phone
number. TAPI also takes into account any settings you have made to your modem properties
in the Control Panel. For example, if you have call waiting enabled and the code to disable
call waiting is *70, TAPI prepends *70 to the dialable address string when you call
TranslateAddress.
3
4
See also: Dial
TrimSeconds
run-time property
6
property TrimSeconds : Integer
Default: 2
! Sets the number of seconds of silence to detect when recording wave files.
Wave recording can be terminated in one of three ways. First, you can manually terminate
recording by calling StopWaveRecord. Second, recording will automatically terminate when
the amount of time specified by MaxMessageLength has passed. Finally, wave recording can
terminate as a result of silence detected on the line.
When TrimSeconds is set to a non-zero value, the wave data is examined as it is recorded.
Silence is determined by comparing the average of the wave data for one second to a silence
threshold as defined by the SilenceThreshold property. If TrimSeconds seconds of silence is
detected, the OnTapiWaveSilence event is generated. If no OnTapiWaveSilence event is
defined then the recording is stopped and the call is terminated. Even after a hangup, a
telephone line contains a good deal of random noise so it is not guaranteed that silence will
be detected immediately after a hangup.
See also: OnTapiWaveSilence, SilenceThreshold, StartWaveRecord, StopWaveRecord
7
8
9
10
11
12
13
14
15
16
17
TApdTapiDevice Component 437
1
1
1
UseSoundCard
run-time property
property UseSoundCard : Boolean
2
Default: False
! Determines where the output from PlayWaveFile is sent.
3
4
5
6
7
UseSoundCard determines whether the output from PlayWaveFile goes to the TAPI device
or to the sound card. By default the output is sent to the TAPI waveform audio device
(through the phone). Set UseSoundCard to True to play the wave file through the sound
card.
The following example plays a wave file through the sound card rather than over the phone
line and then resets the device so that subsequent sounds are played through the TAPI
device:
ApdTapiDevice1.UseSoundCard := True;
ApdTapiDevice1.PlayWaveFile('Call01.wav');
ApdTapiDevice1.UseSoundCard := False;
See also: PlayWaveFile
8
9
WaveFileName
read-only, run-time property
property WaveFileName : TFileName
! The name of the current wave file.
10
11
If a wave file is currently playing, WaveFileName is the name of the file. If no wave file is
currently playing, WaveFileName is the name of the last wave file that was played.
WaveFileName is automatically set when you use PlayWaveFile to play a file.
The following example sets a label’s Caption to the name of the current wave file:
12
13
Label1.Caption := ApdTapiDevice1.WaveFileName;
See also: PlayWaveFile
14
15
16
17
438 Chapter 12: TAPI Components
1
1
WaveState
read-only, run-time property
1
property WaveState : TWaveState
TWaveState = (wsIdle, wsPlaying, wsRecording, wsData);
2
! The current state of the TAPI waveform device.
3
The possible values for wsData are:
Value
Meaning
wsIdle
The wave device is not in use.
wsPlaying
The wave device is playing a wave file.
wsRecording
The wave device is recording a wave file.
wsData
Data is available in the wave buffer.
4
6
The following example stops a wave file if one is currently playing:
7
if ApdTapiDevice1.WaveState = wsPlaying then
ApdTapiDevice1.StopWaveFile;
See also: PlayWaveFile, StartWaveRecord, StopWaveFile, StopWaveRecord
8
9
10
11
12
13
14
15
16
17
TApdTapiDevice Component 439
1
1
1
TApdAbstractTapiStatus Class
2
TApdAbstractTapiStatus is an abstract class that defines the methods and properties needed
by a component that automatically displays status while TApdTapiDevice is dialing or
answering a call. You generally won’t need to create a descendent class of your own, since
Async Professional supplies one, the TApdTapiStatus component (see page 443).
3
4
5
6
7
8
9
10
11
12
However, TApdTapiStatus shows a particular set of information about a call in a predefined
format. If this format is not suitable for your needs, you can create your own descendant of
TApdAbstractTapiStatus. The best way to start is to study the source code of TApdTapiStatus
(in the AdTStat unit) and its form, TStandardTapiDisplay.
The TApdAbstractTapiStatus class contains an instance of a TForm that holds controls used
to display the dial or answer status. You design the form, create an instance, and assign the
instance to the Display property of TApdAbstractTapiStatus.
TApdAbstractTapiStatus overrides the standard VCL properties Ctl3D, Position, and Visible
and the standard VCL method Show. When these routines are used in the status component,
the overridden versions perform the same actions on the associated Display form. Thus you
can display the status form by calling Show, erase it by setting Visible to False, adjust its
position by assigning to Position, and use 3D effects by setting Ctl3D to True.
Once you create an instance of your TApdAbstractTapiStatus descendant, you must assign it
to the StatusDisplay property of your TApdTapiDevice component. When TApdTapiDevice
needs to update the status display, it calls the UpdateDisplay method of
TApdAbstractTapiStatus, which you must override to update your status window.
Hierarchy
TComponent (VCL)
! TApdBaseComponent (OOMisc) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
TApdAbstractTapiStatus (AdTapi)
13
14
Properties
Display
15
16
CreateDisplay
440 Chapter 12: TAPI Components
1
! Version
Methods
17
1
TapiDevice
DestroyDisplay
UpdateDisplay
Reference Section
CreateDisplay
1
dynamic abstract method
2
procedure CreateDisplay; dynamic; abstract;
! An abstract method that creates a form to display dialing and answering status.
3
A descendant of TApdAbstractTapiStatus must override this method with a routine that
creates a TForm component that contains various controls (typically of type TLabel) for
displaying the call progress. The TForm should also contain a TButton control and
associated OnClick event handler that allow the user to cancel the dial or answer operation.
4
CreateDisplay must then assign the instance of this form to the Display property.
See also: DestroyDisplay, Display
DestroyDisplay
6
dynamic abstract method
7
procedure DestroyDisplay; dynamic; abstract;
! An abstract method that destroys the display form.
8
A descendant of TApdAbstractTapiStatus must override this method to destroy the TForm
instance created by CreateDisplay.
9
See also: CreateDisplay, Display
Display
run-time property
property Display : TForm
10
11
! A reference to the form created by CreateDisplay.
CreateDisplay must assign a properly initialized instance of a TForm to this property.
UpdateDisplay can refer to this property to update the status window.
12
See also: CreateDisplay, UpdateDisplay
13
TapiDevice
property
14
property TapiDevice : TApdCustomTapiDevice
! The TApdTapiDevice component that is using the status component.
When you derive components from TApdAbstractTapiStatus, you will probably reference
TApdTapiDevice properties to display information about the progress of the dial or answer
operation. Use this property to do so. It is automatically initialized when you assign the
status component to the StatusDisplay property of TApdTapiDevice.
15
16
17
TApdAbstractTapiStatus Class 441
1
1
1
2
3
4
5
6
7
8
UpdateDisplay
procedure UpdateDisplay(First, Last : Boolean;
Device, Message, Param1, Param2, Param3 : DWORD);
virtual; abstract;
! An abstract method that writes the contents of the status window.
A descendant of TApdAbstractTapiStatus must override this method to update the display
form. The TApdTapiDevice component calls this method regularly from its OnTapiStatus
event handler.
On the first call to UpdateDisplay, First equals True and UpdateDisplay should call the Show
method of Display to draw the outline and background of the status form. On the last call to
UpdateDisplay, Last equals True and UpdateDisplay should set the Visible property of
Display to False to erase the status window.
For all other calls to UpdateDisplay, First and Last are both False. During these calls
UpdateDisplay should update the various labels in the Display form. To get information
about the dial or answer operation, use the TapiDevice field of TApdAbstractTapiStatus to
read the values of various properties such as Number and Dialing.
The CancelClick event handler, if one is provided, should call the CancelCall method of
TApdTapiDevice to abort the dial or answer operation.
9
10
11
12
13
14
15
16
17
442 Chapter 12: TAPI Components
1
1
virtual abtract method
TApdTapiStatus Component
1
TApdTapiStatus is a descendant of TApdAbstractTapiStatus that implements a standard
TAPI status display. To use it, just create an instance and assign it to the StatusDisplay
property of your TApdTapiDevice component. TApdTapiStatus includes all of the most
frequently used information about a call and it provides a Cancel button so that the user can
abort the call at any time.
2
TApdTapiStatus overrides all the abstract methods of TApdAbstractTapiStatus.
TApdTapiStatus has no methods that you must call or properties that you must adjust. You
might want to change the settings of the Ctl3D and Position properties to modify the
appearance and placement of the window.
4
Figure 12.1 shows the TStandardTapiDisplay form that is associated with a TApdTapiStatus
component.
6
3
7
8
9
10
11
12
13
Figure 12.1: TStandardTapiDisplay form.
For an example of using a TApdTapiStatus component, see either the dial or answer
examples in “Making calls” on page 400 and “Answering calls” on page 402.
14
15
16
17
TApdTapiStatus Component 443
1
1
1
Hierarchy
TComponent (VCL)
2
3
TApdBaseComponent (OOMisc) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
TApdAbstractTapiStatus (AdTapi) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 440
TApdTapiStatus (AdTStat)
4
5
6
7
8
9
10
11
12
13
14
15
16
17
444 Chapter 12: TAPI Components
1
1
TApdTapiLog Class
1
TApdTapiLog is a small class that can be associated with a TApdTapiDevice to provide
automatic TAPI logging services. Simply create an instance of TApdTapiLog and assign it to
the TapiLog property of the TApdTapiDevice.
2
TApdTapiLog creates or appends to a text file whose name is given by the TapiHistoryName
property. Each time the OnTapiLog event is generated, the associated TApdTapiLog instance
opens the file, writes a new line to it, and closes the file.
3
4
Following is a sample of the text file created by TApdTapiLog:
5/7/96
5/7/96
5/7/96
5/7/96
10:11:34
10:11:34
10:11:53
10:11:53
PM
PM
PM
PM
: call started
:
dialing 262-6000
:
cancelled
: call finished
5/7/96
5/7/96
5/7/96
5/7/96
5/7/96
5/7/96
5/7/96
10:50:50
10:50:50
10:51:02
10:51:02
10:51:07
10:51:11
10:51:11
PM
PM
PM
PM
PM
PM
PM
: call started
:
dialing 262-6000
:
busy
:
dial failed
:
dialing 262-6000
:
cancelled
: call finished
5/7/96
5/7/96
5/7/96
5/7/96
11:11:34
11:11:34
11:11:53
11:30:07
PM
PM
PM
PM
: call started
:
dialing 262-6000
:
connected
: call finished
6
7
8
9
10
11
Hierarchy
12
TComponent (VCL)
! TApdBaseComponent (OOMisc) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
13
TApdTapiLog (AdTapi)
14
Properties
TapiHistoryName
TapiDevice
! Version
15
Methods
16
UpdateLog
17
TApdTapiLog Class 445
1
1
1
Reference Section
TapiDevice
property
2
property TapiDevice : TApdCustomTapiDevice
3
! The TAPI component that is using the log component.
4
TapiDevice is automatically initialized when the TapiLog property of the owning TAPI
component is set. TapiDevice can be changed to assign the log component to a different
TAPI component.
5
TapiHistoryName
property
property TapiHistoryName : string
6
7
8
9
10
Default: “APROTAPI.HIS”
! Determines the name of the TAPI log file.
The value of TapiHistoryName should be set before calling Dial or AutoAnswer. However,
because the log file is opened and closed for each update, TapiHistoryName can be changed
at any time. If TapiHistoryName is set to an empty string, automatic logging is disabled until
a non-empty string is assigned.
See also: TApdTapiDevice.AutoAnswer, TApdTapiDevice.Dial
UpdateLog
virtual method
procedure UpdateLog(const Log : TTapiLogCode); virtual;
11
12
TTapiLogCode = (ltapiNone, ltapiCallStart,
ltapiCallFinish, ltapiDial, ltapiAnswer, ltapiConnect,
ltapiCancel, ltapiDrop, ltapiBusy, ltapiDialFail);
! Called for each TAPI logging event.
13
14
The Log parameter has the same values passed to the OnTapiLog event handler of
TApdTapiDevice. UpdateLog creates or appends to the log file, builds and writes a text
string for each event, and closes the log file.
15
TApdTapiLog contains a field named TapiDevice that UpdateLog uses to obtain additional
information (e.g., Number and Dialing) about the dial or answer operation.
See also: TApdTapiDevice.OnTapiLog
16
17
446 Chapter 12: TAPI Components
1
1
Chapter 13: Modem Components
1
2
Async Professional provides a modem database and several components for configuring,
dialing, and answering modems and managing phone books. These components are ideal
for controlling modems since they include a modem database and all of the features a
modem-based program might need. Your users simply select their modem (or the closest
match) from the database and your application loads the associated configuration strings
for that modem from the database.
3
4
The modem components provide routines for retrieving modem information from the
libmodem database, manipulating a modem by sending commands through TApdComPort
components, and processing response data, a dialing engine for making repeated dial
attempts, and a few user-interface components for maintaining phonebooks and dialing the
modem. The following units and components are described in this chapter.
5
6
For projects ported from previous versions of Async Professional, the TAdModem,
TApdSModem and supporting components are still available as deprecated components.
7
AdMdm
8
Contains a component (TAdModem) that provides a simple interface for accessing the most
commonly used modem operations. It combines the features of most of the other modem
components into one component.
9
10
AdLibMdm
Contains TApdLibModem, a VCL component that provides an interface to the libmodem
modem database.
11
AdMdmDlg
12
Contains a modem status dialog for use with the TAdModem component.
13
14
15
16
17
447
1
1
modemcap and libmodem
1
The TAdModem component uses a Windows version of libmodem to obtain modem
configuration information from the modemcap database. Modemcap is a set of XML
documents that defines configuration, response and identification information for a wide
range of modems. The following section describes the TApdLibModem class that retrieves
information from the modemcap database. Refer to the libmodem help pages for a more
detailed description of modemcap and libmodem.
2
3
4
6
7
8
9
10
11
12
13
14
15
16
17
modemcap and libmodem 448
1
1
TApdLibModem Component
1
The TApdLibModem component is a Windows implementation of libmodem. This class
provides several routines for access and maintaining lists of known modems and details on
individual modems.
2
Detailed configuration, response and feature information for a wide variety of modems is
stored in numerous XML documents. By default, these documents are stored in the
/etc/modemcap directory. The modemcap.xml document contains an index of all available
modem definitions.
Several data structures are used to describe the modem cap index and the modem detail
files. These types are defined in the AxLibMdm.pas unit. The structures and methods that
are most applicable to the TAdModem are listed here with a brief description of the
structure. Refer to the libmodem source code (AdLibMdm.pas) and help pages for a more
detailed discussion of these structures.
The TApdLibModem class uses the following structures. Several fields are TLists, each item
points to an item in a sequence, the type of item is provided following the field name.
{ an entry from modemcap.xml describing the location and
identification of a single modem }
PLmModemName = ^TLmModemName;
TLmModemName = record
ModemName
: string;
Manufacturer : string;
Model
: string;
ModemFile
: string;
end;
{ a modem response }
PLmResponseData = ^TLmResponseData;
TLmResponseData = record
Response
: string;
ResponseType
: string;
end;
3
4
6
7
8
9
10
11
12
13
14
15
16
17
TApdLibModem Component 449
1
1
{ lots of modem responses }
PLmResponses = ^TLmResponses;
TLmResponses = record
OK
NegotiationProgress
Connect
Error
NoCarrier
NoDialTone
Busy
NoAnswer
Ring
VoiceView1
VoiceView2
VoiceView3
VoiceView4
VoiceView5
VoiceView6
VoiceView7
VoiceView8
RingDuration
RingBreak
Date
Time
Number
Name
Msg
SingleRing
DoubleRing
TripleRing
Voice
Fax
Data
Other
end;
1
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
TList;
TList;
TList;
TList;
TList;
TList;
TList;
TList;
TList;
TList;
TList;
TList;
TList;
TList;
TList;
TList;
TList;
TList;
TList;
TList;
TList;
TList;
TList;
TList;
TList;
TList;
TList;
TList;
TList;
TList;
TList;
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
LmResponseData
LmResponseData
LmResponseData
LmResponseData
LmResponseData
LmResponseData
LmResponseData
LmResponseData
LmResponseData
LmResponseData
LmResponseData
LmResponseData
LmResponseData
LmResponseData
LmResponseData
LmResponseData
LmResponseData
LmResponseData
LmResponseData
LmResponseData
LmResponseData
LmResponseData
LmResponseData
LmResponseData
LmResponseData
LmResponseData
LmResponseData
LmResponseData
LmResponseData
LmResponseData
LmResponseData
2
3
4
6
7
8
9
10
11
12
13
{ a modem command }
PLmModemCommand = ^TLmModemCommand;
TLmModemCommand = record
Command
: string;
Sequence
: Integer;
end;
14
15
{ fax commands and responses }
TLmFaxClassDetails = record
ModemResponseFaxDetect
: string;
ModemResponseDataDetect
: string;
16
17
TApdLibModem Component 450
1
1
SerialSpeedFaxDetect
SerialSpeedDataDetect
HostCommandFaxDetect
HostCommandDataDetect
ModemResponseFaxConnect
ModemResponseDataConnect
AnswerCommand
end;
:
:
:
:
:
:
:
string;
string;
string;
string;
string;
string;
TList;
1
2
3
{ more fax commands and responses }
TLmFaxDetails = record
ExitCommand
: string;
PreAnswerCommand
: string;
PreDialCommand
: string;
ResetCommand
: string;
SetupCommand
: string;
EnableV17Recv
: string;
EnableV17Send
: string;
FixModemClass
: string;
FixSerialSpeed
: string;
HighestSendSpeed
: string;
LowestSendSpeed
: string;
HardwareFlowControl
: string;
SerialSpeedInit
: string;
Cl1FCS
: string;
Cl2DC2
: string;
Cl2lsEx
: string;
Cl2RecvBOR
: string;
Cl2SendBOR
: string;
Cl2SkipCtrlQ
: string;
Cl2SWBOR
: string;
Class2FlowOff
: string;
Class2FlowHW
: string;
Class2FlowSW
: string;
FaxClass1
: TLmFaxClassDetails;
FaxClass2
: TLmFaxClassDetails;
FaxClass2_0
: TLmFaxClassDetails;
end;
{ supported wave formats }
PLmWaveFormat = ^TLMWaveFormat;
TLmWaveFormat = record
ChipSet
: string;
Speed
: string;
SampleSize
: string;
end;
4
6
7
8
9
10
11
12
13
14
15
16
17
TApdLibModem Component 451
1
1
{ wave details }
TLmWaveDriver = record
BaudRate
WaveHardwareID
WaveDevices
LowerMid
LowerWaveInPid
LowerWaveOutPid
WaveOutMixerDest
WaveOutMixerSource
WaveInMixerDest
WaveInMixerSource
WaveFormat
end;
{ voice modem properties }
TLmVoiceSettings = record
VoiceProfile
HandsetCloseDelay
SpeakerPhoneSpecs
AbortPlay
CallerIDOutSide
CallerIDPrivate
TerminatePlay
TerminateRecord
VoiceManufacturerID
VoiceProductIDWaveIn
VoiceProductIDWaveOut
VoiceSwitchFeatures
VoiceBaudRate
VoiceMixerMid
VoiceMixerPid
VoiceMixerLineID
CloseHandset
EnableCallerID
EnableDistinctiveRing
GenerateDigit
HandsetPlayFormat
HandsetRecordFormat
LineSetPlayFormat
LineSetRecordFormat
OpenHandset
SpeakerPhoneDisable
SpeakerPhoneEnable
SpeakerPhoneMute
SpeakerPhoneSetVolumeGain
1
:
:
:
:
:
:
:
:
:
:
:
string;
string;
string;
string;
string;
string;
string;
string;
string;
string;
TList;
2
3
4
// LmWaveFormat
6
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
string;
Integer;
string;
string;
string;
string;
string;
string;
string;
string;
string;
string;
Integer;
string;
string;
string;
:
:
:
:
:
:
:
:
:
:
:
:
:
TList;
TList;
TList;
TList;
TList;
TList;
TList;
TList;
TList;
TList;
TList;
TList;
TList;
7
8
9
10
11
12
//
//
//
//
//
//
//
//
//
//
//
//
//
LmModemCommand;
LmModemCommand;
LmModemCommand;
LmModemCommand;
LmModemCommand;
LmModemCommand;
LmModemCommand;
LmModemCommand;
LmModemCommand;
LmModemCommand;
LmModemCommand;
LmModemCommand;
LmModemCommand;
13
14
15
16
17
TApdLibModem Component 452
1
1
SpeakerPhoneUnMute
StartPlay
StartRecord
StopPlay
StopRecord
VoiceAnswer
VoiceDialNumberSetup
VoiceToDataAnswer
WaveDriver
end;
:
:
:
:
:
:
:
:
:
TList;
// LmModemCommand;
TList;
// LmModemCommand;
TList;
// LmModemCommand;
TList;
// LmModemCommand;
TList;
// LmModemCommand;
TList;
// LmModemCommand;
TList;
// LmModemCommand;
TList;
// LmModemCommand;
TLmWaveDriver;
1
2
3
4
{ lots of specialized modem commands }
TLmModemSettings = record
Prefix
: string;
Terminator
: string;
DialPrefix
: string;
DialSuffix
: string;
SpeakerVolume_High
: string;
SpeakerVolume_Low
: string;
SpeakerVolume_Med
: string;
SpeakerMode_Dial
: string;
SpeakerMode_Off
: string;
SpeakerMode_On
: string;
SpeakerMode_Setup
: string;
FlowControl_Hard
: string;
FlowControl_Off
: string;
FlowControl_Soft
: string;
ErrorControl_Forced
: string;
ErrorControl_Off
: string;
ErrorControl_On
: string;
ErrorControl_Cellular
: string;
ErrorControl_Cellular_Forced: string;
Compression_Off
: string;
Compression_On
: string;
Modulation_Bell
: string;
Modulation_CCITT
: string;
Modulation_CCITT_V23
: string;
SpeedNegotiation_On
: string;
SpeedNegotiation_Off
: string;
Pulse
: string;
Tone
: string;
Blind_Off
: string;
Blind_On
: string;
CallSetupFailTimer
: string;
InactivityTimeout
: string;
CompatibilityFlags
: string;
6
7
8
9
10
11
12
13
14
15
16
17
TApdLibModem Component 453
1
1
ConfigDelay
end;
: Integer;
{ modem hardware settings }
TLmModemHardware = record
AutoConfigOverride
:
ComPort
:
InvalidRDP
:
IoBaseAddress
:
InterruptNumber
:
PermitShare
:
RxFIFO
:
RxTxBufferSize
:
TxFIFO
:
Pcmcia
:
BusType
:
PCCARDAttributeMemoryAddress:
PCCARDAttributeMemorySize
:
PCCARDAttributeMemoryOffset :
end;
{ the whole shebang }
PLmModem = ^TLmModem;
TLmModem = record
Inheritance
AttachedTo
FriendlyName
Manufacturer
Model
ModemID
InactivityFormat
Reset
DCB
Properties
ForwardDelay
VariableTerminator
InfPath
InfSection
ProviderName
DriverDesc
ResponsesKeyName
Default
CallSetupFailTimeout
InactivityTimeout
SupportsWaitForBongTone
SupportsWaitForQuiet
SupportsWaitForDialTone
1
2
string;
string;
string;
Integer;
Integer;
Boolean;
string;
Integer;
string;
string;
string;
Integer;
Integer;
Integer;
3
4
6
7
8
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
9
string;
string;
string;
string;
string;
string;
string;
string;
string;
string;
Integer;
string;
string;
string;
string;
string;
string;
string;
Integer;
Integer;
Boolean;
Boolean;
Boolean;
10
11
12
13
14
15
16
17
TApdLibModem Component 454
1
1
SupportsSpeakerVolumeLow
SupportsSpeakerVolumeMed
SupportsSpeakerVolumeHigh
SupportsSpeakerModeOff
SupportsSpeakerModeDial
SupportsSpeakerModeOn
SupportsSpeakerModeSetup
SupportsSetDataCompressionNegot
SupportsSetErrorControlProtNegot
SupportsSetForcedErrorControl
SupportsSetCellular
SupportsSetHardwareFlowControl
SupportsSetSoftwareFlowControl
SupportsCCITTBellToggle
SupportsSetSpeedNegotiation
SupportsSetTonePulse
SupportsBlindDial
SupportsSetV21V23
SupportsModemDiagnostics
MaxDTERate
MaxDCERate
CurrentCountry
MaximumPortSpeed
PowerDelay
ConfigDelay
BaudRate
Responses
Answer
Fax
FaxDetails
Voice
Hangup
Init
Monitor
Settings
Hardware
BaudRates
Options
end;
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
Boolean;
Boolean;
Boolean;
Boolean;
Boolean;
Boolean;
Boolean;
Boolean;
Boolean;
Boolean;
Boolean;
Boolean;
Boolean;
Boolean;
Boolean;
Boolean;
Boolean;
Boolean;
Boolean;
Integer;
Integer;
string;
Integer;
Integer;
Integer;
Integer;
TLmResponses;
TList;
TList;
TLmFaxDetails;
TLmVoiceSettings;
TList;
TList;
TList;
TLmModemSettings;
TLmModemHardware;
TStringList;
TStringList;
Not all of the proceeding structures and fields are used in Async Professional, they are
included for future enhancements.
1
2
3
4
6
7
8
9
10
11
12
13
14
15
16
17
TApdLibModem Component 455
1
1
Hierarchy
1
TComponent (VCL)
2
TApdLibModem(AdLibMdm)
Properties
3
ModemCapFolder
4
Methods
AddModem
DeleteModem
GetModems
AddModemRecord
DeleteModemRecord
SelectModem
CreateNewDetailFile
GetModem
6
7
8
9
10
11
12
13
14
15
16
17
TApdLibModem Component 456
1
1
Reference Section
AddModem
1
method
2
function AddModem(
const ModemDetailFile : string; Modem : TLmModem) : Integer;
! Adds a modem definition to modemcap.
AddModem adds the modem specified by Modem to the modem detail file specified by
ModemDetailFile. This function will fail if the modem detail file already contains a modem
with the same FriendlyName as Modem. The return value will be an ELmXxx error constant
describing the result of the call to modemcap.
3
4
See also: AddModemRecord
AddModemRecord
method
7
function AddModemRecord(
ModemRecord : TLmModemRecord) : Integer;
! Adds a modem record to the list of available modems.
8
AddModemRecord adds the modem record specified by ModemRecord to the list of
available modems to the modemcap index. This function will fail if the modem cap index
already contains a modem with the same name, manufacturer, and modem as
ModemRecord. The return value will be an ELmXxx error constant describing the result of
the call to modemcap.
See also: AddModem
CreateNewDetailFile
6
9
10
11
method
12
function CreateNewDetailFile(
const ModemDetailFile : string) : Integer;
13
! Creates a new modem detail file.
The CreateNewDetailFile method creates a new modem detail file with the appropriate XML
headers.
14
ModemDetailFile is the name of the new modem detail file. If ModemDetailFile already
exists, it will be overwritten. The result of this method is ecOK if the file was created
successfully.
15
16
17
TApdLibModem Component 457
1
1
DeleteModem
method
function DeleteModem(
const ModemDetailFile : string; Modem : TLmModem) : Integer;
! Deletes a modem detail record from a modem list.
DeleteModem will search the modem detail file specified by ModemDetailFile for the
modem pointed to by Modem. If the modem is found, it will be removed from the list. The
return value will be an ELmXxx error constant describing the result of the call to
modemcap.
1
2
3
4
See also: DeleteModemRecord
DeleteModemRecord
method
6
function DeleteModemRecord(
ModemRecord : TLmModemRecord) : Integer;
7
! Deletes a modem record from a modemcap index.
DeleteModemRecord will search the modemcap index for the modem record pointed to by
ModemRecord. If the modem is found it will be removed from the list. The return value will
be an ELmXxx error constant describing the result of the call to modemcap.
8
See also: DeleteModem
9
GetModem
method
function GetModem(const ModemDetailFile,
ModemName : string; var Modem : TLmModem) : Integer;
! Retrieves a specific modem from the modem detail file.
GetModem retrieves a modem definition from modemcap. ModemDetailFile is the name of
the modem detail file where the definition resides; ModemName is the name of the modem
to retrieve. Modem is the TLmModem structure that contains the details of the modem.
If this method is successful, the return value is ecOK. If the ModemDetailFile is not found,
the return value is ecFileNotFound. If a modem matching ModemName is not found in the
ModemDetailFile, the return value is ecModemNotFound.
See also: SelectModem
10
11
12
13
14
15
16
17
TApdLibModem Component 458
1
1
GetModems
method
function GetModems(
const ModemDetailFile : string) : TStringList;
1
2
! Retrieves all modems from a modem detail file.
GetModems retrieves all modem definitions contained in a specific modem detail file.
ModemDetailFile is the name of the modem detail file. If successful, the return value is a
TStringList containing the details of all modems contained in ModemDetailFile. If
GetModems fails, the return value will be nil.
3
4
The return value is a TStringList containing an item for each modem contained in the
modem detail file. The Strings portion of the item is the “friendly name” of the modem. The
Objects portion is a TLmModem structure defining the modem.
ModemCapFolder
property
property ModemCapFolder : string
6
7
! Defines the location of the modemcap modem database.
ModemCapFolder determines where the TApdLibModem component will find the
modemcap modem database. Set ModemCapFolder to the name of the folder where
modemcap was installed (default installation is in the C:\APRO\MODEMCAP folder).
SelectModem
8
9
method
function SelectModem(
var ModemFile, ModemManufacturer, ModemName: string;
var LmModem : TLmModem) : Boolean;
10
11
! Displays modem selection dialog box.
Call the SelectModem method to display the modem selection dialog and select a modem
definition. This method is used by the TAdModem.SelectModem method, and is
documented here to allow customization of the selection process.
The ModemFile and ModemName parameters are used to filter the displayed modems, as
well as to retrieve the specifications of the selected modem. If ModemFile is not empty, only
modems contained in that modem detail file will be displayed. If ModemName is not empty,
only modems with that name will be displayed. ModemName is case-sensitive.
12
13
14
15
16
17
TApdLibModem Component 459
1
1
If the OK button is selected to close the dialog, ModemFile will contain the name of the
modem detail file where the selected modem resides; ModemManufacturer is the
manufacturer, and ModemName is the name of the modem; LmModem is the TLmModem
structure that defines the modem capabilities and configuration information.
1
2
See also: GetModem
3
4
6
7
8
9
10
11
12
13
14
15
16
17
TApdLibModem Component 460
1
1
TAdModem Component
1
The TAdModem component combines the features of libmodem-based device selection and
configuration and modem control methods. TAdModem integrates the selection of the
modem from the modem database and the dialog to show the current status of the modem.
The TAdModem component is used to select and configure a modem and to dial or answer
the line.
2
There are many similarities between the TAdModem and TApdTapiDevice components.
The TApdTapiDevice uses the Microsoft TAPI library, and most of TAPI operates under the
“black box” principle. The TAdModem can be more flexible, especially when custom
modem or port configurations must be used.
4
The TAdModem component requires libmodem and the modemcap database. See
“TApdLibModem Component” on page 449, and the appropriate help pages, for details
concerning these libraries.
6
3
7
Modem selection
When using the TAdModem component, the appropriate modem definition must be loaded
from modemcap. To do this, call the TAdModem.SelectModem method, which will display
the dialog box shown in Figure 13.1.
8
9
10
11
12
Figure 13.1: TAdModem.SelectModem dialog box.
The “Manufacturer” combo box will be filled with a list of known modem manufacturers
(those that are included in modemcap). Select a manufacturer from the list and the “Modem
name” combo box will be filled with a list of known modems from the appropriate modem
detail file. Select the modem name from the list and click OK.
To select a modem programatically, assign a TAdModemNameProp object to the
SelectedDevice property
13
14
15
16
17
TAdModem Component 461
1
1
Connectionless connections
1
The TAdModem can establish a connection direct to the port through the ConfigAndOpen
method. This does not establish a connection, except to the local modem. When the device
has been configured, the OnModemConnect event will be generated and you can
communicate with the modem.
2
3
Dialing
The TAdModem can also establish connections by dialing. To dial a number through the
modem, and have the modem negotiate a connection, call the TAdModem.Dial method.
The phone number to dial is passed in the ANumber parameter to the Dial method. This
number is dialed without modification. If you need to enter a prefix to gain an outside line,
to use a calling card, or other non-dialable use, those digits must be added to ANumber. For
example, if you want to dial “555-1212” but you need to dial ‘9’ to access an outside line,
ANumber would be “9 555-1212”.
4
6
7
Answering an incoming call
The TAdModem can also establish a connection by answering an incoming call. The
TAdModem will answer incoming calls by monitoring the port for the modem’s ring
indicator, which is usually a “RING” response. The AnswerOnRing property of the
TAdModem component determines how many of these responses to receive before issuing
the answer command to the modem. The RingWaitTimeout property determines the
number of milliseconds to wait after receiving the last ring before assuming that the caller
terminated the connection attempt. In the USA, the ring indicators are sent approximately
every six seconds, the default RingWaitTimeout (1200 ms) will wait for about two ring
cycles. If another ring indicator is not received, the internal RingCount property is reset, and
the TAdModem will wait for the next AnswerOnRing ring indicators. Note that the port
associated with the TAdModem must be open when waiting for an incoming call, which will
prevent other processes from accessing the serial port. To share the port while passively
waiting for calls, use the TApdTapiDevice.
When the modem dials the number, or answers an incoming call, the modem will attempt to
establish a data connection with the remote modem. If the modem can establish a mutually
supported set of connection parameters (baud, data compression, error correction, etc.) the
OnModemConnect event will be generated. If the modem could not establish a connection,
the OnModemFail event will be generated. The FailureCode property will contain a code
indicating the reason for the failure.
8
9
10
11
12
13
14
15
16
17
TAdModem Component 462
1
1
Hierarchy
1
TComponent (VCL)
! TApdBaseComponent(OOMisc) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
TAdCustomModem(AdMdm)
2
3
TAdModem(AdMdm)
4
Properties
AnswerOnRing
FailureCode
PassthroughMode
BPSRate
LastResponse
RetryWait
CancelCall
MaxAttempts
RingWaitTimeout
ComPort
ModemCapFolder
SelectedDevice
Dialing
ModemState
DialTimeout
NegotiationResponses
StatusDisplay
! Version
Methods
6
7
8
AutoAnswer
FailureCodeMsg
SetDevConfig
CancelCall
GetDevConfig
ModemLogToString
ConfigAndOpen
SendCommand
ModemStatusMsg
Dial
SelectDevice
TranslateAddress
Events
9
10
11
OnModemCallerID
OnModemDisconnect
OnModemLog
OnModemConnect
OnModemFail
OnModemStatusMethods
12
13
14
15
16
17
TAdModem Component 463
1
1
Reference Section
1
AnswerOnRing
property
2
property AnswerOnRing : Byte
!
Default: 2
3
The number of times the component should allow the incoming call to ring before
answering it.
4
The AnswerOnRing property determines the number of “RING” responses that the
TAdModem detects before answering the incoming call. Once the AutoAnswer method is
called, internal TApdDataPackets are initialized to detect the “RING” response from the
modem when an incoming call is signaled. When AnswerOfRing “RING” responses are
detected, the call is answered.
6
Caller ID information is transmitted between the first and second rings in most countries.
The AnswerOnRing property defaults to 2 to avoid problems with Caller ID information
corrupting the connection negotiations.
7
See also: AutoAnswer, RingWaitTimeout
8
AutoAnswer
method
9
procedure AutoAnswer;
! Prepares the modem to answer a call after a specified number of rings.
After AutoAnswer sets the appropriate variables and triggers, control returns to the program
and the modem component watches for incoming calls in the background. If
AnswerOnRing “RING” responses are received from the modem, the call is answered. If
StatusDisplay is assigned to a TApdAbstractModemStatus component, that status dialog is
displayed during an answer attempt.
Once the “RING” response is received, the ModemState changes from
msAutoAnswerBackground to msAutoAnswerWait. ModemState remains in
msAutoAnswerWait until AnswerOnRing “RING” responses are received, when
ModemState enters the msAnswerWait state and the call is answered. When a connection is
established, ModemState changes to msConnected and the OnModemConnect event is
generated.
10
11
12
13
14
15
16
17
TAdModem Component 464
1
1
Auto answer mode can be cancelled by calling CancelCall. The auto answer mode is
cancelled regardless of whether the TAdModem is waiting in the background for the
incoming call or the modem is currently answering the call.
1
Calling AutoAnswer does not turn on the auto answer (AA) light on an external modem.
The AutoAnswer method does not use the auto answer feature of the modem.
2
See also: CancelCall, ModemState, OnModemCallerID, OnModemConnect,
OnModemFail, StatusDisplay
3
BPSRate
read-only, run-time property
4
property BPSRate : DWORD
! The rate of the current call in bits per second.
BPSRate is the rate negotiated between the local and remote modems for the current call. If a
call is not in progress, BPSRate returns zero. BPSRate is determined by the extended
connection responses provided by the modem, and may take the connection’s negotiated
data compression into account. This property is available once the OnModemConnect event
is generated.
7
8
See also: OnModemConnect
CancelCall
6
method
9
procedure CancelCall;
10
! Terminates a connection attempt or disconnects the current call.
If a dial or an answer attempt is in progress, calling this method aborts the attempt and
returns the modem to its normal state.
11
If you call CancelCall while the TAdModem component is in auto answer mode, the modem
component is taken out of auto answer mode.
12
CancelCall is the TAdModem universal method for terminating the current call. It can be
used while waiting for an incoming call, answering an incoming call, dialing a call, or during
an established connection. TAdModem terminates the current process, assures that the
modems have disconnected, and places the modem into an idle state (not waiting for calls).
13
14
CancelCall returns when the connection, or connection attempt, has terminated. The
OnModemDisconnect event is generated if a connection was present when CancelCall was
called; if a connection was not present, no event is generated.
15
See also: AutoAnswer, Dial, OnModemDisconnect
16
17
TAdModem Component 465
1
1
ComPort
property
1
property ComPort : TApdCustomComPort
! Determines the serial port to which the modem is connected.
2
ComPort is usually set automatically at design time to the first TApdComPort component
the TAdModem finds on the form. If you need to, you can use the Object Inspector to select
a different TApdComPort component.
Setting the ComPort property at run time is necessary only when using a dynamically
created TApdComPort or when selecting among several TApdComPort components.
3
4
Note that some properties of the TApdComPort may be overridden with properties
retrieved from modemcap when the TAdModem initializes the modem.
See also: TApdCustomComPort
6
ConfigAndOpen
method
7
procedure ConfigAndOpen;
! Configures the modem and provides access to the modem without a connection.
The ConfigAndOpen method configures the modem according to the configuration
settings retrieved from modemcap. Once the modem has been configured, the
OnModemConnect event is generated.
ConfigAndOpen is used primarily to configure the modem to a known state where it can be
used without a connection. For example, you can use ConfigAndOpen to provide terminal
access to a modem for diagnostics or for faxing.
While the modem is being configured, several TApdDataPackets will be initialized to
capture the responses from the modem. Once the modem has been configured, the
TApdDataPackets will be removed, providing no further notification of the state of the
modem.
8
9
10
11
12
Note that ConfigAndOpen establishes a connection to the port, not through the modem to
another device.
13
See also: AutoAnswer, CancelCall, Dial, OnModemConnect
14
15
16
17
TAdModem Component 466
1
1
Dial
method
1
procedure Dial(ANumber : string);
! Dials the specified telephone number.
2
Dial initializes the modem and then dials the number specified by the ANumber parameter.
When the connection is established, the OnModemConnected event is generated.
3
Several TApdDataPackets are initialized to detect modem responses during initialization
and to monitor connection progress.
4
Dial returns immediately. The OnModemConnect event is generated when the appropriate
connection responses have been received from the modem. The OnModemFail event is
generated if the modem could not be initialized, could not detect dial tone (if that was
required by the configuration), if the modems could not negotiate a mutually acceptable set
of connection parameters, or if the remote party did not answer the call within DialTimeout
seconds. The FailureCode property can be queried to determine the actual reason for the
failure.
6
7
A dial operation can be cancelled at any time by calling CancelCall.
The ANumber parameter to this event should contain all parameters required for your
modem to dial the number. This includes any prefixes required by your phone system to
obtain an outside line. For example, if your phone system requires you to dial ‘9’ to get an
outside line, the call to Dial would include that digit:
ApxModem1.Dial('9 555-1212');
8
9
10
See also: AutoAnswer, CancelCall, ConfigAndOpen, DialTimeout, OnModemConnect,
OnModemFail, StatusDisplay
DialTimeout
11
property
12
property DialTimeout : Integer
Default: 60
! The number of seconds to wait for a connection after dialing the number.
When a dial attempt begins, the TAdModem component allows DialTimeout seconds for a
connection result to be received from the modem. The timeout countdown starts when the
Dial method is called. If the connection responses are not received within this time, a
timeout occurs, the operation is cancelled and the OnModemFail event is generated.
See also: Dial, FailureCode, OnModemFail
13
14
15
16
17
TAdModem Component 467
1
1
FailureCode
read-only, run-time property
1
property FailureCode : Integer
! The numerical code indicating the last failure.
2
FailureCode is the result of the last AutoAnswer, ConfigAndOpen, or Dial method that was
called. FailureCode indicates the nature of the failure.
3
This property is used primarily in the OnModemFail event handler to determine the reason
for the failure.
4
See also: AutoAnswer, ConfigAndOpen, Dial, FailureCodeMsg, OnModemFail
FailureCodeMsg
method
function FailureCodeMsg(const FailureCode : Integer) : string;
6
! Converts a numerical FailureCode into a string describing the error.
7
The FailureCodeMsg method converts a FailureCode into a human-readable string
describing the error.
8
See also: FailureCode, OnModemFail
GetDevConfig
method
9
function GetDevConfig : TLmModem;
10
! Returns the modem configuration structure.
The modemcap database file contains a list of modems and their configuration. The
GetDevConfig method returns the currently active configuration structure for the selected
modem.
11
This method can be used to confirm the modem configuration prior to use, or to make
changes to the current configuration.
12
See also: SetDevConfig
13
14
15
16
17
TAdModem Component 468
1
1
ModemCapFolder
property
1
property ModemCapFolder : string
2
Default: Empty string
! The name of the directory where the modemcap modem database has been installed.
The modemcap database file contains a list of modems and their configuration strings. This
folder is used when SelectDevice is called to display a list of modems from which the user
can choose. ModemCapFolder is also used at design time to display the list of modems
when the SelectedDevice property is modified.
3
4
See also: SelectDevice, SelectedDevice
ModemLogToString
method
6
function ModemLogToString(
const LogCode : TAdModemLogCode) : string;
7
! Returns an English string describing an error code.
The ModemLogToString method references the string resource for the log code and returns
a text description of the code. This method is used primarily in the OnModemLog event to
display a text description of the log event.
See also: OnModemLog
ModemState
read-only, run-time property
8
9
10
property ModemState : TAdModemState
TApdModemState = (msUnknown, msIdle, msInitializing,
msAutoAnswerBackground, msAutoAnswerWait, msAnswerWait,
msDialWait, msDialCycle, msConnectWait, msConnected,
msHangup, msCancel);
! The current state of the TAdModem component.
11
12
13
Default: msUnknown
ModemState is used internally to track modem responses and controlling the state of the
TAdModem component for configuration, dialing, and answering. This property is made
public for possible use of status routines.
14
15
16
17
TAdModem Component 469
1
1
ModemState values are:
1
Value
Description
msUnknown
Hasn’t been or couldn’t be initialized.
msIdle
Idle and ready.
msInitializing
Starting initialize process.
msAutoAnswerBackground
Autoanswer mode -- no rings received.
msAutoAnswerWait
Autoanswer mode -- waiting for Nth ring.
msAnswerWait
Answering call -- waiting for connect.
msDialWait
Dialing call -- waiting for connect.
msConnectWait
Connect in progress -- waiting for optional
data.
msConnected
Done with connect process.
msHangup
Starting hangup process.
msCancel
Starting cancel process.
3
4
6
7
8
See also: ModemStateMsg, OnModemStatus
NegotiationResponses
2
read-only, run-time property
9
property NegotiationResponses : TStringList
10
! Contains the modem’s reported negotiated connection parameters.
During a connection attempt, either answering or dialing, the modem may return several
lines of text describing the negotiated connection parameters. These responses may indicate
the error correction, data compression, or other features that the modem negotiated. These
features are informative in nature and are provided in the NegotiationResponses property.
During an AutoAnswer operation, NegotiationResponses will contain all modem responses
from the TAdModem sending the answer command to the final connection response.
During a Dial operation, NegotiationResponses will contain all modem responses from the
TAdModem sending the dial command to the final connection response.
11
12
13
14
See also: AutoAnswer, Dial
15
16
17
TAdModem Component 470
1
1
OnModemCallerID
method
1
property OnModemCallerID : TModemCallerIDEvent
TModemCallerIDEvent = procedure(
Modem : TAdCustomModem; ID, IDName : string) of object;
2
! Defines an event handler that is generated when Caller ID information is detected.
3
If the modemcap structure supports Caller ID configurations, the modem will be
appropriately initialized to respond to the Caller ID signals provided for incoming calls.
Internal TApdDataPackets will be initialized to detect the Caller ID responses from the
modem. When Caller ID responses are detected, the OnModemCallerID event will be
generated.
Modem is the TAdCustomModem component that generated the event. ID is the
identification reported by the Caller ID signal for the number field (usually the caller’s
phone number). IDName is the identification reported by the Caller ID signal for the name
field (usually the caller’s subscribed name).
In most countries, the telephone company supplies the Caller ID information between the
first and second rings. A typical format is shown in the following:
4
6
7
8
DATE: MM/DD/YY<CR><LF>
9
TIME: HH:MM:SS<CR><LF> {24-hour format}
NUMBER: {variable content}<CR><LF>
10
NAME: {variable content}<CR><LF>
Some telephone companies provide information in a different order, different format, or
even different information entirely. The TAdModem will detect the NUMBER and NAME
responses. Additional responses can be gathered using your own TApdDataPackets.
11
12
13
14
15
16
17
TAdModem Component 471
1
1
OnModemConnect
method
1
property OnModemConnect : TModemNotifyEvent
2
TModemNotifyEvent = procedure(
Modem : TAdCustomModem) of object;
! Defines an event handler that is generated when a connection is established.
3
The OnModemConnect event is generated when dialing or answering after the modem
returns the connection response. This event is also generated after a call to ConfigAndOpen
once the modem has been configured.
4
Modem is the TAdCustomModem component that generated the event. No other
parameters are provided.
The TAdModem will watch for the connection responses as defined by the modemcap entry
for the selected modem and by monitoring the DCD signal.
6
See also: AutoAnswer, ConfigAndOpen, Dial, OnModemDisconnect
7
OnModemDisconnect
method
8
property OnModemDisconnect : TModemNotifyEvent
TModemNotifyEvent = procedure(
Modem : TAdCustomModem) of object;
9
! Defines an event handler that is generated when a connection is terminated.
The OnModemDisconnect event is generated when the TAdModem detects the connection
has been terminated. When the TAdModem detects that the connection has been
established, a status trigger monitoring for changes in DCD is installed. When DCD is
lowered, the connection is considered terminated, and the OnModemDisconnect event is
generated. This event is also generated when the CancelCall method successfully terminates
the connection.
10
11
12
Modem is the TAdCustomModem that generated the event. No other parameters are
provided.
13
Note that some devices and protocols routinely toggle DCD. For these situations, consult the
device and protocol documentation for details on detecting connection termination.
14
See also: AutoAnswer, ConfigAndOpen, Dial, OnModemConnect
15
16
17
TAdModem Component 472
1
1
OnModemFail
method
1
property OnModemFail : TModemNotifyEvent
2
TModemNotifyEvent = procedure (
Modem : TAdCustomModem) of object;
! Defines an event handler that is generated when a modem or connection failure is detected.
The OnModemFail event is generated if the modem could not be initialized, or if a
connection could not be established. When dialing, this event is generated if the modem
could not detect dial tone (if the configuration required dial tone), if the modems could not
negotiate a mutually acceptable set of connection parameters, or if the remote party did not
answer the call within DialTimeout seconds. When answering, this event is generated if the
modems could not negotiate a mutually acceptable set of connection parameters, of if the
connection attempt timed out.
Modem is the TAdCustomModem that generated the event. No other parameters are
provided. The reason for the failure can be obtained from the FailureCode property.
3
4
6
7
See also: AutoAnswer, ConfigAndOpen, Dial, FailureCode, FailureCodeMsg
OnModemLog
method
property OnModemLog : TModemLogEvent
8
9
TModemLogEvent = procedure(
Modem : TAdCustomModem; LogCode : TApdModemLogCode) of object;
! Defines an event handler that is generated at designated points during a dial or answer
attempt.
The primary purpose of this event is to give the application a chance to log auditing
information about telephone calls and whether they succeed or fail. This event is intended
primarily for high-level logging, not to determine program flow.
Modem is the TAdCustomModem that generated the event. LogCode is the
TAdModemLogCode that described the event being logged.
10
11
12
13
14
15
16
17
TAdModem Component 473
1
1
TAdModemLogCode can have one of the following values:
Value
Description
mlNone
None
mlDial
Dialing
mlAutoAnswer
Initiated AutoAnswer
mlAnswer
Answering an incoming call
mlConnect
Connected
mlCancel
Call cancelled
mlBusy
Called number was busy
mlConnectFail
Connection attempt failed
1
2
3
4
6
See also: ModemLogToString
OnModemStatus
method
7
property OnModemStatus : TModemStatusEvent
TModemStatusEvent = procedure(
Modem : TAdCustomModem; ModemState : TAdModemState) of object;
! Defines an event handler that is generated when the state of the component changes.
8
9
The OnModemStatus event is generated periodically when the ModemState property is
changed. This event indicates when the state of the modem or connection is changed.
10
Modem is the TAdCustomModem that generated the event. ModemState is the new state of
the component. See the ModemState definition for a list of possible ModemState values and
their meanings.
11
See also: ModemState, ModemStatusMsg
12
RingCount
run-time, read-only property
13
property RingCount : Byte
14
Default: 0
! The number of ring signals detected for the current call.
When the TAdModem is in AutoAnswer mode, the RingCount property indicates the
number of ring signals that have been detected. When a ring signal has not been detected,
RingCount will be 0. When RingCount equals AnswerOnRing, the call will be answered.
15
16
See also: AutoAnswer, RingWaitTimeout
17
TAdModem Component 474
1
1
RingWaitTimeout
property
1
property RingWaitTimeout : DWORD
2
Default: 1200
! Determines the number of milliseconds to wait before resetting an AutoAnswer attempt.
Most phone companies generate the ring indicator signal every six seconds. When the
TAdModem is in AutoAnswer mode, consecutive ring signals within RingWaitTimeout
milliseconds are considered to be from the same call, and will increment the internal ring
counter. If RingWaitTimeout milliseconds elapse after the ring signal, the caller is assumed
to have aborted the call, and the internal ring counter is reset.
3
4
See also: AutoAnswer, RingCount
SelectDevice
method
function SelectDevice : Boolean;
6
7
! Displays the modem selection dialog.
SelectDevice displays the modem selection dialog, which lists the modems defined in the
modemcap database. This method will return True if a device is selected, and False if the
modem selection dialog box is cancelled.
To select a modem from modemcap, the manufacturer and modem name must be selected.
The “Manufacturer” combo box will contain a list of all manufacturers that are included in
modemcap. When a manufacturer is selected, the “Modem name” combo box will contain a
list of all modems from that manufacturer in modemcap. The OK button will be disabled
until this requirement is satisfied. When the “OK” button is clicked, this method returns
True and the SelectedDevice property will be updated to reflect the selected device. If the
Cancel button is clicked, this method returns False and SelectedDevice is not updated.
If the AutoAnswer, ConfigAndOpen, or Dial methods are called without a valid modem
specified in SelectedDevice, the modem selection dialog will be displayed. If a modem is not
selected, the ecNoSelectedDevice exception will be raised.
8
9
10
11
12
13
Selecting a new modem configuration through the SelectedDevice property of the
SelectDevice method while a connection is established will raise the ecModemBusy
exception.
14
See also: GetDevConfig, SelectedDevice, SetDevConfig
15
16
17
TAdModem Component 475
1
1
SelectedDevice
property
1
property SelectedDevice : TAdModemNameProp
2
TAdModemNameProp = class(TPersistent)
published
property Manufacturer : string;
property Name : string;
end;
3
! The currently selected modem.
4
SelectedDevice displays the modem manufacturer, model, and name of the modem that has
been selected through the SelectDevice method. The properties of the
TAdModemNameProp are read-only at design time and are available to determine which
modemcap structure will be used.
To change modems configurations at run time, you may either call the SelectDevice method
to display the device selection dialog or assign a TAdModemNameProp class to this
property.
Selecting a new modem configuration through the SelectedDevice property or SelectDevice
method while a connection is established will raise the ecModemBusy exception.
See also: GetDevConfig, SelectDevice, SetDevConfig
6
7
8
9
SendCommand
method
function SendCommand(const Command : string) : Boolean;
! Provides a convenient method to send a custom command to the modem.
The SendCommand method can be used to send a modem command to the modem.
SendCommand returns when the modem either responds to the command, or times out. If
the modem returns a successful response, the return value will be True. If the modem
returns a failure response, the return value will be False and the FailureCode property will
indicate the type of failure based on the response.
10
11
12
13
See also: FailureCode, FailureCodeMsg
14
15
16
17
TAdModem Component 476
1
1
SetDevConfig
method
1
procedure SetDevConfig(const Config : TLmModem);
! Forces a modem configuration structure.
2
The modemcap database file contains a list of modems and their configuration. The
SetDevConfig method forces a new configuration structure, which will be used in
subsequent calls to the AutoAnswer, ConfigAndOpen, and Dial methods.
3
This method is intended to be used after the GetDevConfig method provides the default
configuration structure for the modem. Additional configuration settings, or port options,
can be defined for the session. Changes made through SetDevConfig will remain in effect
until the SelectedDevice is changed or the application is terminated. This method will not
change the modem definition in the modemcap database.
4
See also: GetDevConfig
6
StatusDisplay
property
7
property StatusDisplay : TApdAbstractModemStatus
8
! The status dialog used to provide visual status indications.
During AutoAnswer or Dial operations, the status dialog specified by the StatusDisplay
property can provide visual feedback to indicate what the modem is doing.
The provided TAdModemStatus component encompasses several status indicators to
illustrate the state of the connection. A custom status display can be used instead of the
TAdModemStatus component. See the description of the TAdModemStatus component for
details.
9
10
11
See also: OnModemStatus
12
13
14
15
16
17
TAdModem Component 477
1
1
TAdModemStatus Component
1
The TAdModemStatus component is a descendant of the TApdAbstractModemStatus class
that implements a standard modem status dialog. To use it, create an instance of the
component and assign it to the StatusDisplay property of a TAdModem component. The
TAdModemStatus component displays common status indicators as well as a detailed
history of what the TAdModem is doing. The TAdModemStatus component provides a
Cancel button, which will cancel an AutoAnswer or Dial attempt in progress.
2
3
4
Figure 13.2 shows the dialog box that the TAdModemStatus component displays.
6
7
Figure 13.2: TAdModemStatus dialog box.
The Status label displays a string describing the current TAdModem.ModemState property
value, and indicates what the TAdModem is doing. The Using label displays the
TAdModem.SelectedDevice.Name property value and the serial port that is being used. The
Elapsed time label displays the number of seconds that have elapsed since the current
operation began.
The Cancel button will cancel the operation by calling the CancelCall method of the
TAdModem component.
8
9
10
11
12
13
14
15
16
17
TAdModemStatus Component 478
1
1
The down arrow button will display a more detailed status dialog, as shown in Figure 13.3.
1
2
3
4
6
Figure 13.3: TAdModemStatus expanded dialog box.
To return to the compact display, click the up arrow button.
7
To create a custom status dialog box, the OnModemStatus event of the TAdModem
component can be used to update a separate form in your application, or to update a status
bar. You can also create a new component descending from the TApdAbstractModemStatus
class. The UpdateStatus method of the TApdAbstractModemStatus class is automatically
called by the TAdModem component whenever the OnModemStatus event of the
TAdModem component is generated. This method must be implemented to update your
custom dialog. See the AxMdmDlg.pas unit for details on creating a custom dialog box.
Hierarchy
8
9
10
11
TComponent (VCL)
! TApdBaseComponent(OOMisc) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
12
TApdAbstractModemStatus(AdMdmDlg)
13
TAdModemStatus(AdMdmDlg)
14
Properties
Caption
Started
StatusDialog
15
! Version
16
Methods
UpdateDisplay
17
TAdModemStatus Component 479
1
1
Reference Section
1
Caption
property
2
property Caption : string
Default: “Modem status”
!
3
Determines the caption for the status dialog box.
The Caption property sets the StatusDialog.Caption property. If your window manager
supports title bars on dialogs, changing this property will change the caption displayed in
the title bar. If your window manager does not support title bars on dialog boxes, this
property has no visible effect.
4
See also: StatusDialog
6
Started
run-time, read-only property
property Started : Booleanproperty Started : Boolean
Default: False
7
8
! Indicates whether the status dialog box has been created and initialized.
The TForm descendant specified by the StatusDialog component is created and initialized
when the UpdateDisplay method is called for the first time. Started indicates whether
StatusDialog has been created. When the StatusDialog is no longer needed, the form is
destroyed and Started is set to False.
Started is used by the TAdModemStatus dialog, it is available, but not required, for custom
status dialogs.
See also: StatusDialog, UpdateDisplay
9
10
11
12
13
14
15
16
17
TAdModemStatus Component 480
1
1
StatusDialog
property
1
property StatusDialog : TForm
! Determines the status form to display.
2
The TAdModemStatus component defines the TAdModemStatusDialog for displaying
modem status. The dialog box that the TAdModemStatus component provides is illustrated
and described in the overview section on page 461.
The UpdateDisplay method updates StatusDialog to provide status indications. The dialog
properties can be changed at run time by referencing the StatusDialog property. For
example, changing the TAdModemStatus.StatusDialog.BorderStyle property will change
the dialog’s BorderStyle.
To provide a different dialog, create a new component, descend from the
TApdAbstractModemStatus class, and define a new TForm descendant for your display.
See also: UpdateDisplay
UpdateDisplay
3
4
6
7
method
procedure UpdateDisplay(
Modem : TAdCustomModem; const Str0, Str1, Str2, Str3 : string);
8
9
! Updates the StatusDialog.
The UpdateDisplay method updates the dialog specified by StatusDialog. When creating a
custom display, this method must be overridden to update the controls available on the
custom form.
10
Modem is the TAdCustomModem whose status has changed. Str0, Str1, Str2 and Str3 are
strings that can be used to provide additional status indicators for the display.
11
See also: StatusDialog
12
13
14
15
16
17
TAdModemStatus Component 481
1
1
1
2
3
4
6
7
8
9
10
11
12
13
14
15
16
17
TAdModemStatus Component 482
1
1
Chapter 14: File Transfer Protocols
1
2
Many communications applications need to transfer files or other large amounts of data
from one machine to another. This could be accomplished by having the sender call
PutBlock repeatedly and the receiver call GetBlock correspondingly. However, the
application would have a tremendous amount of detail work still to do. It would need logic
to transfer file name and size information, to check for and recover from transmission
errors, to handle file I/O, etc., etc.
3
4
That’s why Async Professional provides standard, tested, reliable, high performance file
transfer protocols. The term “protocol” means that both sides of the communication link
behave in a clear, well-defined manner following agreed-upon rules. The rules vary among
the different protocols and some protocols offer more control and features than others. At a
minimum, each protocol handles file I/O and serial port I/O and checks for errors. Some
protocols also include error correcting logic, multi-file transfers, and automatic recovery
after partial file transfers.
5
6
7
Async Professional offers the most widely used industry standard file transfer protocols, as
shown in Table 14.1.
8
Table 14.1: Available Async Professional file transfer protocols
9
Protocol
Component
Description
Xmodem
TApdProtocol
128 byte blocks with checksum block checking.
See “Xmodem” on page 501 for more information.
10
XmodemCRC
TApdProtocol
128 byte blocks with CRC block checking. See
“Xmodem” on page 501 for more information.
11
Xmodem1K
TApdProtocol
1024 byte blocks with CRC block checking. See
“Xmodem” on page 501 for more information.
Xmodem1KG
TApdProtocol
Streaming Xmodem1K. See “Xmodem” on page 501
for more information.
Ymodem
TApdProtocol
1024 byte blocks, batch. See “Ymodem” on
page 504 for more information.
YmodemG
TApdProtocol
Streaming Ymodem. See “Ymodem” on page 504 for
more information.
Zmodem
TApdProtocol
1024 byte blocks, batch, streaming,
restartable. See “Zmodem” on page 507 for more
information.
12
13
14
15
16
17
483
1
1
1
Table 14.1: Available Async Professional file transfer protocols (continued)
Protocol
Component
Description
2
Kermit
TApdProtocol
80 byte blocks, batch, with long blocks and
windowing. See “Kermit” on page 513 for more
information.
3
ASCII
TApdProtocol
ASCII stream with inter-character and interline delays. See “ASCII” on page 519 for more
information.
4
FTP
TApdFTPClient
An internet file transfer protocol. See “FTP”
on page 521 for more information.
5
6
7
Three related classes are also described in this chapter. TApdAbstractStatus defines a
mechanism by which the protocol can report its status (percent completion, transfer rate,
etc.) to the user. TApdProtocolStatus derives from TApdAbstractStatus to present protocol
status in a particular style. TApdProtocolLog is a small component that writes to a log file
the status of each file transferred by an associated TApdProtocol component.
8
9
10
11
12
13
14
15
16
17
484 Chapter 14: File Transfer Protocols
1
1
General Issues
1
The Async Professional protocol engine is implemented in a group of units with names like
AwAbsPcl (abstract protocol services), AwXmodem (Xmodem protocol), AwKermit
(Kermit protocol), etc. These units are linked directly into your application. The
TApdProtocol component, implemented in the AdProtcl unit, is a shell around the protocol
engine.
2
The protocol engine works in the background by using Async Professional timer, data
available, and data match triggers. Windows can continue with other tasks while a file
transfer is in progress as long as the other tasks yield properly for other events.
4
3
The following subsections document issues that arise for all types of file transfers that use
the Async Professional protocol engine. Note that the following general issues do not relate
to the TApdFTPClient component.
6
Buffer sizes
7
When a TApdComPort component is created, you specify the input and output buffer sizes
that the Windows communications driver is to use. An interactive communications process
such as a terminal window can safely use small buffer sizes, say 4K bytes for input and
output. A protocol file transfer requires larger buffers. Why? Consider how a file transfer
program is likely to be used under Windows.
8
9
Once a file transfer starts, it is likely that the user will work in another window until the file
transfer finishes. Because the transfer is running as a background application, it is at the
mercy of other Windows tasks. Many Windows applications and built-in Windows
operations can hog the CPU to an extent that prevents the background transfer from
succeeding.
10
To different degrees, all file transfer protocols are time-critical. They must respond to
incoming events in a timely fashion, usually within a few seconds. If they fail to respond in
the required time, the remote protocol software times out and repeats the failed operation.
Such timeouts and repetitions at best reduce the protocol transfer rate and at worst can
cause the protocol to fail.
12
In practice, it takes a very ill-behaved program or unusual user (e.g., someone who spends
30 seconds to move a window) to cause most protocols to fail. But this can happen and your
application should do whatever it can to minimize the chances.
One of the things your program can do is use a large input buffer. The Windows
communications driver continues to receive data and store it in the input buffer even if the
associated application program isn’t getting any time to run. With a 30K byte input buffer
and a data rate of 1600 characters per second, the buffer can hold 19 seconds worth of
11
13
14
15
16
17
General Issues 485
1
1
1
2
3
4
5
6
7
8
9
10
11
incoming data before overflowing. When the application eventually regains control it
processes all received data before relinquishing control. The input buffer is then ready to
hold another 19 seconds worth of data.
A large output buffer is also valuable when transmitting files. Streaming protocols such as
YmodemG and Zmodem, and even Kermit to a lesser degree, typically transmit until they
fill the output buffer, then relinquish control. They don’t regain control until the buffer
drains enough to hold another data block and Windows can process the associated status
trigger message.
If the status trigger message is delayed because another Windows application didn’t yield,
the Windows communications driver continues to transmit the data remaining in the output
buffer. Using the same numbers as the input buffer example, the driver can transmit
independently for up to 19 seconds before running out of data.
When transmitting files under protocol control, the output buffer size must be at least 2078
bytes (i.e., 2K plus 30 bytes). This size is required because the protocols send protocol data
to the port by copying the entire block into the output buffer. If the block size is 1024 bytes
and every character is escaped (preceded by a special character that prevents the link from
misinterpreting the data as a control sequence), the output buffer must hold 2048 bytes. The
extra 30 bytes is a safety margin provided so that the protocol doesn’t need to check for
output buffer space for block check characters and a subsequent header, if there is one. The
protocols use the direct copy approach because it’s much faster than calling PutChar for
each character in the block.
In summary, your application should use input and output buffers that are as small as
possible. However, the smallest possible buffer might actually be quite large, even as large as
32K, for applications that are designed to run in the background.
Protocol events
12
The protocol component generates several kinds of events. General descriptions of these
events follow:
13
OnProtocolAccept
14
15
16
procedure(CP : TObject; var Accept : Boolean;
var FName : string) of object;
Generated as soon as the protocol window knows the name of an incoming file. This
provides an opportunity to accept or reject the file, or to change its name. See the
OnProtocolAccept event on page 544 for details about how to handle this event. Also see
“AcceptFile processing” on page 495.
17
486 Chapter 14: File Transfer Protocols
1
1
OnProtocolError
procedure(CP : TObject; ErrorCode : SmallInt) of object;
Generated when an unrecoverable error occurs. Recoverable errors do not generate this
message because such errors are a routine part of transferring files and the failed operation
is retried automatically. See OnProtocolError (page 545) for details. Also see ““Error
handling” on page 488.
1
2
3
OnProtocolFinish
procedure(CP : TObject; ErrorCode : SmallInt) of object;
4
Generated after all files have been transferred or after the protocol terminates due to an
unrecoverable error. This event also sends the final result code of the protocol.
OnProtocolLog
6
procedure(CP : TObject; Log : Word) of object;
Generated at the start and end of transferring each file. This provides an opportunity to log
the status of each file transfer. See OnProtocolLog (page 546) for details. Also see “Protocol
logging” on page 493.
7
OnProtocolNextFile
8
procedure(CP :TObject; var FName : string) of object;
Generated whenever it is time to transmit another file. By default this message is handled by
the protocol component, which returns the name of the next file that matches the FileMask
property. Programs can intercept this event to provide other ways to choose the next file. See
OnProtocolNextFile (page 547) for details. Also see “NextFile processing” on page 494.
OnProtocolStatus
9
10
11
procedure(CP : TObject; Options : Word) of object;
Generated at regular intervals so that programs can display the progress of the protocol. See
OnProtocolStatus (page 548) for details. Also see ““Protocol status” on page 489.
12
13
Aborting a protocol
There will certainly be times when a protocol in progress must be canceled (e.g., when
something goes wrong at the remote computer or the user simply decides not to continue
the transfer). Async Professional protocols provide for this situation with the
CancelProtocol method.
To cancel any protocol simply call the CancelProtocol method of the TApdProtocol
component. This method sends an appropriate cancel sequence to the remote computer and
terminates. The protocol component remains intact, ready to handle additional protocol
transfers.
14
15
16
17
General Issues 487
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
When protocol transfers take place over a modem link it is a good idea to monitor the DCD
(data carrier detect) line and abort the protocol if carrier is lost. The DCD line goes high
when modems first connect and remains high until one of the modems hangs up.
Occasionally, line noise or other disturbances in the telephone network break the
connection between the modems, causing DCD to go low.
Some protocols quickly detect that the remote isn’t acknowledging after the connection is
broken. These protocols soon abort with an error code of ecTimeout or ecTooManyErrors.
By contrast, streaming protocols can take a very long time to notice that the connection is
broken because they don’t require acknowledgments.
The protocol component provides an option to handle dropped carrier automatically. Set
the AbortNoCarrier property to True before calling StartTransmit or StartReceive and the
protocol engine automatically aborts if the DCD line is not high at any point during the
protocol. The protocol cancels itself immediately and generates an OnProtocolFinish event
with an error code of ecAbortNoCarrier.
Using the AbortNoCarrier property is better than checking DCD and calling
CancelProtocol in your own code. When you do this, the protocol engine sends a cancel
sequence to the remote computer. If hardware flow control is enabled and the modem has
lowered the DSR or CTS signals as well as DCD, the protocol waits several seconds before
deciding it can’t send the cancel command, leading to an unnecessary delay for the
application. The AbortNoCarrier option prevents the protocol engine from sending the
cancel sequence, so the protocol stops immediately.
Error handling
All protocol transfers are subject to errors, including parity errors, files not found, and other
file I/O errors. Whenever possible the protocol window handles errors internally by retrying
an operation or requesting the remote computer to retry. At some point, however, it
determines that the situation is unrecoverable and generates an OnProtocolError event. An
application should include a handler for this event. Following is a simple example:
procedure Form1.ApdProtocol1ProtocolError(
CP : TObject; ErrorCode : SmallInt);
begin
ShowMessage('Fatal protocol error: ' + ErrorMsg(ErrorCode));
end;
15
This event handler’s sole task is to display a message about the error. ErrorMsg is a function
from the AdExcept unit that returns an English string for any Async Professional error code.
16
See “Error Handling and Exception Classes” on page 900 for additional information about
errors.
17
488 Chapter 14: File Transfer Protocols
1
1
Protocol status
1
A protocol transfer can last a few seconds or several hours depending on the size and speed
of the transfer. Because the protocol component handles the details of the transfer from start
to finish, your application’s code is not executing during this entire time. You and your users
certainly want to know what’s happening as the transfer progresses, so Async Professional
provides a hook for your application to regularly regain control during this time.
During a protocol transfer the protocol window frequently generates an OnProtocolStatus
event. This gives your code the opportunity to monitor and display the progress of the
protocol. Following are code fragments that illustrate how:
TForm1 = class(TForm)
...
FN: TLabel;
BT: TLabel;
BR: TLabel;
...
end;
2
3
4
6
7
procedure TForm1.ApdProtocol1ProtocolStatus(
CP : TObject; Options : Word);
begin
case Options of
apFirstCall :
...do setup stuff
apLastCall :
...do cleanup stuff
else
{show status}
FN.Caption := ApdProtocol.FileName;
BT.Caption := IntToStr(ApdProtocol.BytesTransferred);
BR.Caption := IntToStr(ApdProtocol.BytesRemaining);
end;
end;
The method named ApdProtocol1ProtocolStatus handles the OnProtocolStatus event by
updating a form at each call. The Options parameter passed to this routine can take on two
special values:
apFirstCall = 1;
apLastCall = 2;
8
9
10
11
12
13
14
15
Options is set to apFirstCall the first time the protocol generates the event after being started
by StartTransmit or StartReceive. Options is set to apLastCall the last time it generates the
event, when the protocol is finished. Options equals zero for all other times.
16
17
General Issues 489
1
1
1
The rest of the information about protocol progress is obtained by reading the values of
various TApdProtocol properties, including:
2
BlockCheckMethod - the type of block check calculation used by the protocol. See the
reference section entry for this property (page 530) for a complete description of all block
check types.
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
BlockErrors - the number of errors for the current block. This is the number of times the
protocol has unsuccessfully tried to transmit or receive the current block. It is reset to zero
when the block is finally accepted.
BlockLength - the current transfer block length. Although this value is usually static, some
protocols modify the length of the block on the fly. Zmodem in particular reduces the block
length after several block errors in a row and raises it again after several good blocks.
BlockNumber - the number of blocks transmitted so far. This is obtained by dividing the
number of bytes transferred by the current block length, so it will change if the block length
changes.
BytesRemaining - the size of the file minus BytesTransferred. When the file size isn’t known,
BytesRemaining returns zero.
BytesTransferred - the number of bytes transmitted or received so far. When transmitting,
this number is sometimes only an estimate. The uncertainty comes from the fact that the
protocol window doesn’t know when a particular byte has actually been transferred.
BytesTransferred is the number of bytes the protocol window has transferred to the output
buffer of the communications driver, minus the number of bytes that the driver reports are
currently in the buffer. Unfortunately, this calculation is still imperfect because it’s
impossible to know how much of the output buffer holds actual file data and how much
holds overhead characters needed by the protocol. Each protocol has a few simple rules it
uses to estimate this proportion, which in practice yield good estimates.
ElapsedTicks - the number of ticks elapsed since the protocol started. In order to provide
accurate CPS values, the protocol engine doesn’t start the timer until it receives the first
block from the remote computer.
FileDate - the date and time of the file being transmitted or received. If the protocol does not
support this feature, FileDate returns zero.
FileLength - the size of the file being transmitted or received. For transmitted files the file
size is always known. For received files the file size is known only if the protocol supports
this feature and the receiver has received this information. If the file size is not known,
FileLength returns zero.
FileName - the fully qualified name of the file that is being received or transmitted. When
receiving with a protocol that does not transfer the file name, FileName simply returns the
name previously assigned to it.
490 Chapter 14: File Transfer Protocols
1
1
InitialPosition - used only for resumed file transfers using the Zmodem protocol. To display
an accurate transfer rate (CPS, or character per second, rate), status routines for these
protocols must subtract InitialPosition from BytesTransferred to obtain the actual number
of bytes transferred during this session. If this is not a resumed file transfer, InitialPosition
returns zero.
ProtocolError - the code of the last error encountered by the protocol. This equals zero
except for the first status call after an error is encountered. See “Error Handling and
Exception Classes” on page 900 for additional information.
ProtocolStatus - a code that indicates the current state of the protocol. Table 14.2 shows all of
the possible values. The usual status value is psOK, which means that the protocol is
operating normally. Other status values indicate recoverable error conditions, protocol
resume conditions, protocol start-up states, and internal protocol states. Fatal protocol
errors are not represented by protocol states; they are first reported via the OnProtocolError
event. However, it is possible that a final status message might be sent after a fatal error
occurs.
1
2
3
4
6
7
Table 14.2: Possible values for the ProtocolStatus property of TApdProtocol
Status Code
Value
Explanation
psOK
0
Protocol is OK.
psProtocolHandshake
1
Protocol handshaking in progress.
psInvalidDate
2
Bad date/time stamp received and ignored.
psFileRejected
3
Incoming file was rejected.
psFileRenamed
4
Incoming file was renamed.
psSkipFile
5
Incoming file was skipped.
psFileDoesntExist
6
Incoming file doesn’t exist locally and is
skipped.
psCantWriteFile
7
Incoming file skipped due to Zmodem
options.
psTimeout
8
Timed out waiting for something.
psBlockCheckError
9
Bad checksum or CRC.
psLongPacket
10
Block too long.
psDuplicateBlock
11
Duplicate block received and ignored.
psProtocolError
12
Error in protocol.
psCancelRequested
13
Cancel requested.
psEndFile
14
At end of file.
8
9
10
11
12
13
14
15
16
17
General Issues 491
1
1
1
2
3
4
5
Table 14.2: Possible values for the ProtocolStatus property of TApdProtocol (continued)
Status Code
Value
Explanation
psSequenceError
15
Block was out of sequence.
psAbortNoCarrier
16
Aborting on carrier loss.
psGotCrcE
17
Got Zmodem CrcE packet.
psGotCrcG
18
Got Zmodem CrcG packet.
psGotCrcW
19
Got Zmodem CrcW packet.
psGotCrcQ
20
Got Zmodem CrcQ packet.
6
ProtocolType - the protocol type, which is one of ptXmodem, ptXmodemCRC,
ptXmodem1K, ptXmodem1KG, ptYmodem, ptYmodemG, ptZmodem, ptKermit, or
ptAscii.
7
TotalErrors - the number of errors encountered since the current file was started. It is reset
only when a new file is started.
8
Various properties that describe the option settings for the protocol may also be used within
the status routine. These include HonorDirectory, IncludeDirectory, RTSLowForWrite,
AbortNoCarrier, and other options that are specific to particular protocols.
10
The StatusInterval property, which defaults to 18, is the maximum number of ticks between
OnProtocolStatus events. The protocol generates an OnProtocolStatus event after every
significant event (received a file name, received a complete block, etc.) or after at most
StatusInterval ticks.
11
Async Professional includes a mechanism for providing an automatic protocol status display
without programming, through the TApdProtocol’s StatusDisplay property:
9
12
13
14
15
16
property StatusDisplay : TApdAbstractStatus
The TApdAbstractStatus class is described in more detail beginning on page
TApdAbstractStatus Class. For each OnProtocolStatus event the protocol checks whether
StatusDisplay is assigned. If it is, the protocol calls the UpdateDisplay method of
StatusDisplay to update the display. It then calls the OnProtocolStatus event if one was
implemented.
When a protocol component is created, either dynamically or when dropped on a form, it
searches the form for a TApdAbstractStatus instance and updates the StatusDisplay
property with the first one it finds. It also fills in StatusDisplay when a TApdAbstractStatus
component is added to the form. StatusDisplay can also be modified at design time or run
time to point to a TApdAbstractDisplay component other than the one assigned
automatically.
17
492 Chapter 14: File Transfer Protocols
1
1
Async Professional also provides a non-abstract implementation of a TApdAbstractStatus
class called the TApdProtocolStatus component. If you drop one of these on your form it
will automatically display full status information during all file transfers. See page 582 for
more information.
Protocol logging
1
2
3
File transfer is often an automated process. For example, an application might send all of the
day’s transaction files to a remote computer during the night. In this case the application
would also keep a record of the files that were successfully transmitted and those that
weren’t.
The Async Professional protocol logging feature is ideal for this kind of application. It
provides the opportunity to log information about each received or transmitted file and
whether the transfer succeeded.
To support logging, the protocol component generates on OnProtocolLog event at the start
and end of each file transfer. The event passes a parameter that identifies the current logging
action. Here is an example that handles this event:
procedure TForm1.ApdProtocol1ProtocolLog(
CP : TObject; Log : Word);
begin
case Log of
lfReceiveStart,
lfTransmitStart :
CurrentFile.Caption := ApdProtocol1.FileName;
4
6
7
8
9
10
lfReceiveOk,
lfTransmitOk :
GoodList.Items.Add(ApdProtocol1.FileName);
11
lfReceiveFail,
lfTransmitFail :
BadList.Items.Add(ApdProtocol1.FileName);
12
13
lfReceiveSkip
lfTransmitSkip :
SkipList.Items.Add(ApdProtocol1.FileName);
end;
end;
14
The example shows every possible logging value. The meaning of the various logging
conditions should be clear except for perhaps lfReceiveSkip and lfTransmitSkip.
lfReceiveSkip is generated by any of the protocols when an incoming file is rejected by the
15
16
17
General Issues 493
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
handler for the OnProtocolAccept event. lfTransmitSkip is generated only in a Zmodem
transfer when the remote receiver indicates that it does not want to receive the file being
transmitted by the logging application.
This example uses a TLabel control named CurrentFile to display the name of the file
currently being transmitted. As files are transmitted or received it updates three TListBox
components: GoodList for all successful transfers, BadList for all failed transfers, and
SkipList for all skipped files.
The logging routine isn’t limited to just writing status information. It also can take care of
file-related start-up and cleanup activities. One example of this is to delete partially received
files. You would probably want to do this for all protocols except Zmodem, which can
resume failed transfers from the point of the error without having to retransmit the entire
file.
Async Professional includes a mechanism for providing automatic protocol logging without
programming, through the ProtocolLog property of TApdProtocol:
property ProtocolLog : TApdProtocolLog
The TApdProtocolLog component is described in more detail beginning on page 583.
TApdProtocolLog Component. For each OnProtocolLog event the protocol checks whether
ProtocolLog is assigned. If it is, the protocol calls UpdateLog to write information to the log
file. It then calls the OnProtocolLog event if one was implemented.
When a protocol component is created, either dynamically or when dropped on a form, it
searches the form for a TApdProtocolLog instance and updates the ProtocolLog property
with the first one it finds. It also fills in ProtocolLog when a TApdProtocolLog component is
added to the form. ProtocolLog can also be modified at design time or run time to point to a
TApdProtocolLog component other than the one assigned automatically.
NextFile processing
Several of the protocols provided by Async Professional can transmit and receive batches of
files. When the protocol is ready to transmit a new file it generates an OnProtocolNextFile
event. The event handler responds by returning the name of the next file to transmit, or an
empty string to terminate the batch.
In most cases, you don’t need to worry about handling this event because the protocol
component does so itself. The protocol component determines the next file to send with
DOS filemask processing, using the mask assigned to the FileMask property.
For non-batch protocols like Xmodem the file mask should not contain wildcards. Such
protocols are capable of transmitting only a single file at a time, and if the mask matches
more than one file only the first matching file is transmitted.
17
494 Chapter 14: File Transfer Protocols
1
1
When the filemask technique is not adequate, you can write an event handler that
implements whatever behavior you need.
Here is an OnProtocolNextFile event handler that provides custom NextFile processing.
const
FileIndex : Word = 0;
File1 = 'C:\AUTOEXEC.BAT';
File2 = 'C:\CONFIG.SYS';
File3 = 'C:\ASYNCPRO\ADPORT.PAS';
...
procedure TForm.ApdProtocol1ProtocolNextFile(
CP : TObject; var FName : string);
begin
Inc(FileIndex);
case FileIndex of
1 : FName := File1;
2 : FName := File2;
3 : FName := File3;
else FName := '';
end;
end;
1
2
3
4
6
7
8
This example sends only the three files named by the constants File1, File2, and File3. The
event handler returns each string in sequence. After the three files are transmitted, the
handler returns an empty string to tell the protocol that no more files are available to
transmit.
Async Professional does not provide built-in support for transmitting an arbitrary list of
files. That’s because VCL provides the TStringList class, which can easily be combined with
an OnProtocolNextFile event handler to send any list of files. The EXFLIST example
program shows how.
9
10
11
12
AcceptFile processing
When receiving files, there may be times when you don’t want the incoming file. Consider,
for example, an open BBS where a first time caller is attempting to upload a 10M byte file at
2400 baud. Since this would tie up the BBS for more than 11 hours you probably would want
to refuse it immediately. If the caller is using a protocol that transmits the file size in advance,
you can detect that it’s bigger than you want and refuse the upload.
As another example, suppose that a BBS has a well-publicized rule that it accepts only LZH
uploads and it detects that an incoming file has a ZIP extension. If a caller is using a protocol
that transmits the file name in advance, you can refuse an upload immediately.
13
14
15
16
The OnProtocolAccept event can be used to build such behavior into your application.
17
General Issues 495
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Note that Zmodem, alone among the Async Professional protocols, has built-in
functionality for certain kinds of accept file functions. For example, it can reject an upload
that would overwrite an existing file, or accept it only if the upload’s time stamp is newer
than the existing file. These options are described fully in “Zmodem” on page 507. The
AcceptFile function is more general than this, and applies to all of the Async Professional
protocols.
Once the protocol knows the name of an incoming file, but before it starts receiving data, it
generates an OnProtocolAccept event. A form can respond to the event by setting the Accept
parameter to True to accept the file, or False to refuse it. By default, all files are accepted.
For all protocols except Zmodem, the first rejected file terminates the entire batch transfer.
Zmodem has provisions for skipping files, and the transmitter picks up again with the next
file after the rejected one.
The OnProtocolAccept event also provides an opportunity to rename an incoming file if its
current name isn’t acceptable. For example, if a file name conflicts with an existing file, you
can accept the file but change its name.
Note that all protocols have built-in options for handling incoming file name collisions. See
the WriteFailAction property on page WriteFailAction property for a complete description.
You don’t need to write an OnProtocolAccept event handler if these constants provide the
needed behavior.
Here is an event handler that rejects all files with the ARC extension:
procedure TForm1.ApdProtocol1ProtocolAccept(
CP : TObject; var Accept : Boolean; var FName : string);
begin
Accept := AnsiCompareText(ExtractFileExt(FName), '.ARC') <> 0;
end;
This example examines the extension of the incoming file and sets Accept to False if it
matches “.ARC”. Note that the function can check various properties to obtain additional
information about the file. In particular, the file size is available by reading the FileLength
property for protocols that transmit the size.
Internal logic
The protocol component has so far been described as a black box—you initialize it and call
StartTransmit or StartReceive to perform the protocol “magic.” Now it is time to look inside
the box, at how the protocol engine works. With this additional information, you will be
able to use the protocols more effectively and take better advantage of the events generated
by the protocol window.
17
496 Chapter 14: File Transfer Protocols
1
1
Receiving files
1
When receiving files, the protocol window employs the following logic shown in Figure
14.1. This diagram generally applies to all protocols.
1
No connect
3
handshake
4
2
No file
2
get file name header
3
generate OnProtocolLog
6
4
Fail
generate OnProtocolAccept
Fail
apply WriteFail options
or
apply Zmodem rules
7
5
8
9
6
Fail
open file
10
7
Fail
11
receive data block
write data block
generate OnProtocolStatus
flush to disk every 8K
12
8
13
close file
9
14
generate OnProtocolLog
10
(batch only)
15
end protocol
16
Figure 14.1: Protocol window logic when receiving files.
17
General Issues 497
1
1
1
The protocol first attempts to “handshake” with the remote machine. A handshake consists
of a valid response to an initial character sequence sent by the transmitter. If the handshake
is unsuccessful after a specified number of retries, the protocol ends.
2
If the handshake is successful, the transmitter is asked for the name of the next file to
transmit. It responds by sending a block containing the name.
3
The protocol generates the OnProtocolLog event to give the application an opportunity to
record the file name or take any other special action needed at the start of the transfer. Note
that the logging routine receives the file name before the OnProtocolAccept event handler
has had a chance to modify it.
4
5
6
7
8
Next the protocol generates the OnProtocolAccept event. If the message handler sets Accept
to False, the file is skipped and control is transferred to step 9. Otherwise, the built-in
WriteFail options or Zmodem’s built-in file management rules are applied. If the protocol
fails at this point, control is transferred to step 9.
The received file is created using the name from the file name header, perhaps as modified
by the OnProtocolAccept event handler. Step 6 also allocates work buffers and initializes
several internal variables used to manage an 8K byte receive buffer. If the open fails, control
is transferred to step 8, which disposes of buffers and closes the file.
10
The actual transfer of data comes next in step 7. The internal operations of this step vary
tremendously among the protocols, so it is condensed in this diagram. The
OnProtocolStatus event is generated at least once for each block received. If unrecoverable
errors occur for any reason (user abort, broken connection, disk full, etc.), control is
transferred to step 8.
11
After the file transfer is complete, the file is closed in step 8. Then the OnProtocolLog event
is generated with information regarding whether the file was received OK, rejected, or failed.
9
12
In a batch protocol, control returns to the top of the loop to get another file header. If one is
received, the whole process is repeated. Otherwise, the protocol is ended by coordinating
with the transmitter and cleaning up.
13
14
15
16
17
498 Chapter 14: File Transfer Protocols
1
1
Transmitting files
1
When transmitting files, the protocol window employs the following logic as shown in
Figure 14.2. This diagram generally applies to all protocols.
2
1
No connect
3
handshake
2
No file
4
“Return the next file based on
SendFileName”
3
generate OnProtocolLog
6
4
Fail
open file
7
5
Fail
8
read data block
send data block
generate OnProtocolStatus
reload buffer every 8K
9
6
10
close file
7
generate OnProtocolLog
11
(batch only)
12
8
end protocol
13
Figure 14.2: Protocol window logic when transmitting files.
The protocol first attempts to handshake with the remote machine. A handshake consists of
sending an initial character sequence and waiting for a valid response. If the handshake is
unsuccessful after a specified number of retries, the protocol ends.
If the handshake is successful, the protocol generates an OnProtocolNextFile event. The
default handler of the component returns the next file based on a file mask, or a custom
event handler can return the next file using its own logic.
14
15
16
17
General Issues 499
1
1
1
2
3
4
5
6
7
The protocol generates the OnProtocolLog event to give the application an opportunity to
record the outgoing file name or take any other special action needed at the start of the
transfer.
The outgoing file is opened in step 4. This step also allocates work buffers and initializes
variables used to manage an 8K byte send buffer. If any of these steps fail, control is
transferred to step 6 to clean up.
The actual transfer of data comes next in step 5. The file is read in 8K byte blocks and sent
using the block size native to the protocol. The OnProtocolStatus event is generated at least
once for every block sent. If unrecoverable errors occur, control is immediately transferred
to step 6.
After the file transfer is complete, the file is closed and buffers are disposed in step 6. Then
the OnProtocolLog event is generated with information regarding whether the file was
transferred OK, rejected, or failed.
In a batch protocol, control returns to the top of the loop to get another file to send. If one is
available, the whole process is repeated. Otherwise, the protocol is ended by coordinating
with the receiver and cleaning up.
8
9
10
11
12
13
14
15
16
17
500 Chapter 14: File Transfer Protocols
1
1
Xmodem
1
Xmodem is the oldest protocol supported by Async Professional. It was developed and first
implemented by Ward Christensen in 1977 and placed in the public domain. Since then, it
has become an extremely popular protocol and continues in use today (although at a
diminished frequency).
2
Xmodem is also the simplest, and perhaps the slowest, protocol supported by Async
Professional. Xmodem uses blocks of only 128 bytes and requires an acknowledgment of
each block. It uses only a simple checksum for data integrity.
What follows is a simplified description of the Xmodem protocol, although it describes far
more than is required to actually use the protocol in Async Professional. For additional
details on the internals of the Xmodem protocol, see the YMODEM.DOC file in the
PROTDOC.LZH archive. Figure 14.3 shows the format for XModem blocks.
3
4
6
7
<SOH>
<block#>
not
<block#>
<128 bytes of data>
<checksum>
8
Figure 14.3: The format for XModem blocks.
9
The <SOH> character marks the start of the block. Next comes a one byte block number
followed by a ones complement of the block number. The block number starts at one and
goes up to 255 where it rolls over to zero and starts the cycle again. Following the block
numbers are 128 bytes of data and a one-byte checksum. The checksum is calculated by
adding together all the data bytes and ignoring any carries that result. Table 14.3 describes a
typical Xmodem protocol transfer.
Receiver
<---
<SOH><1><254><128 data bytes><chk>
<SOH><2><253><128 data bytes><chk>
15
<ACK>
--->
<---
13
14
<ACK>
--->
<---
<EOT>
<NAK>
--->
<---
11
12
Table 14.3: Description of a typical XModem protocol transfer
Transmitter
10
16
<ACK>
17
Xmodem 501
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
The receiver always starts the protocol by issuing a <NAK>, also called the handshake
character. It waits 10 seconds for the transmitter to send a block of data. If it doesn’t get a
block within 10 seconds, it sends another <NAK>. This continues for 10 retries, after which
the receiver gives up.
If the receiver does get a block, it compares the checksum it calculates to the received
checksum. If the checksums differ, the receiver sends a <NAK> and the transmitter resends
the block. If the checksums match, the receiver accepts the block by sending an <ACK>.
This continues until the complete file is transmitted. The transmitter signifies this by
sending an <EOT>, which the receiver acknowledges with an <ACK>.
Either side can cancel the protocol at any time by sending three <CAN> characters (^X).
However, during an Xmodem receive the receiver cannot tell whether the <CAN>
characters are real data or a cancel request. The sequence is recognized as a cancel request
only when it comes between blocks. Hence, more than three <CAN> characters are
sometimes required to cancel the receiver. Sufficient characters are required to complete the
current block, then three more <CAN> characters to cancel the protocol.
From this description several things become clear. First, this protocol does not transfer any
information about the file being transmitted. Hence, the receiver must assign a name to the
incoming file.
The receiver also doesn’t know the exact size of the file, even after it is completely received.
The received file size is always a multiple of the block size. This Xmodem implementation
fills the last partial block of a transfer with characters of value BlockFillChar, whose default
is ^Z. .BlockFillChar is a typed constant defined in AwTPcl.pas. To change the default
BlockFillChar, simply assign the new character to that typed constant.
Xmodem often exhibits a start-up delay. The transmitter always waits for a <NAK> from
the receiver as its start signal. If the receiving program was started first, the transmitter
probably missed the first <NAK> and must wait for the receiver to time out and send
another <NAK>.
Xmodem offers no escaping of binary control characters. Escaping means that characters
can be transformed before being transmitted to prevent certain binary data characters, such
as <XON>, from being interpreted as data link control characters. As a result, you can’t use
software flow control in an Xmodem transfer (since the flow control software would
misinterpret <XON> or <XOFF> characters in the data stream as flow control requests) and
Xmodem itself can’t tell the difference between <CAN> characters used for protocol
cancellation and for data.
The only merit of the basic Xmodem protocol is that it is so widespread that it’s probably
supported by any microcomputer communications program you can find, thus providing a
lowest common denominator between systems.
17
502 Chapter 14: File Transfer Protocols
1
1
Xmodem extensions
1
Xmodem has been tweaked and improved through the years. Some of these variations have
become standards of their own and are supported by Async Professional. These Xmodem
extensions are treated as separate protocols enabled by assigning a different value to the
ProtocolType property.
The first of these improvements is called Xmodem CRC, which substitutes a 16 bit CRC
(cyclic redundancy check) for the original checksum. This offers a much higher level of
data integrity. When given the opportunity, you should always choose Xmodem CRC over
plain Xmodem.
The receiver indicates that it wants to use Xmodem CRC by sending the character ‘C’ instead
of <NAK> to start the protocol. If the transmitter doesn’t respond to the ‘C’ within three
attempts, the receiver assumes the transmitter isn’t capable of using Xmodem CRC. The
receiver automatically drops back to using checksums by sending a <NAK>.
Another popular extension is called Xmodem 1K. This derivative builds on Xmodem CRC
by using 1024 byte blocks instead of 128 byte blocks. When Xmodem 1K is active, each
block starts with an <STX> character instead of an <SOH>. Xmodem 1K also uses a 16 bit
CRC as the block check.
A larger block size can greatly speed up the protocol because it reduces the number of times
the transmitter must wait for an acknowledgment. However, it can actually reduce
throughput over noisy lines because more data must be retransmitted when errors are
encountered. The implementation of Xmodem 1K in Async Professional drops back to 128
byte blocks whenever it receives more than 5 <NAK> characters in a row. Once it drops
back to 128 byte blocks, it never tries to step back up to 1024 byte blocks.
The final Xmodem extension supported by Async Professional is Xmodem 1KG. This
streaming protocol is requested when the receiver sends ‘G’ as the initial handshake
character instead of <NAK>. Streaming in this context means that the transmitter
continuously transmits blocks without waiting for acknowledgements. In fact, all blocks are
assumed to be correct and the receiver never sends acknowledgements. If the receiver does
encounter a bad block, it aborts the entire transfer by sending a <NAK>.
You shouldn’t even consider using this streaming protocol unless you are using error
correcting modems with their error control features turned on. In fact, you might want to
have your application refuse to use Xmodem 1KG if error correcting modems aren’t
detected. The OnGotErrCorrection event of the TApdModem component (see page 461)
can be used to detect an error correcting connection. The advantage of a streaming protocol
like Xmodem 1KG is its very high throughput. There is no acknowledgement delay, so the
protocol is extremely efficient. Zmodem has this same property, but can also retry and
recover from errors.
2
3
4
6
7
8
9
10
11
12
13
14
15
16
17
Xmodem 503
1
1
1
Ymodem
2
Ymodem is a derivative of Xmodem that is different enough to be called a unique protocol.
The details and history of Ymodem are fully explained in the file YMODEM.DOC in the
PROTDOC.LZH archive. What follows is a simplified explanation of Ymodem that provides
more than enough information to use it with Async Professional.
3
5
Ymodem is essentially Xmodem 1K with batch facilities added, which means that a single
protocol session can transfer as many files as you care to transmit. Another important
enhancement is that the transmitter can provide the receiver with the incoming file name,
size, and timestamp.
6
Ymodem achieves this by adding block zero to the Xmodem 1K protocol. Block zero is
transferred first and contains file information in the format shown in Figure 14.4.
4
7
<SOH>
<0>
<255>
<name> <0> <len> <0> <date> <0>...<0>
<CRChi> <CRClo>
8
Figure 14.4: File information and format for a YModem block zero transfer.
9
10
11
12
13
The <name> field is the only required field. It supplies the name of the file in lower case
letters. Path information can be included but the protocol requires that all ‘\’ characters be
converted to ‘/’.
The <len> field specifies the file length as an ASCII string. This field allows the receiver to
truncate the received file to discard the filler characters at the end of the last block.
The <date> field is the date and time stamp of the file. It is transmitted as the number of
seconds since January 1, 1970 GMT, expressed in ASCII octal digits (a Unix convention).
Async Professional takes care of properly formatting this block. You don’t need to do
anything but specify the name of the file to transmit. When receiving Ymodem files, Async
Professional uses the information in this block, if present, to adjust the size of the received
file and its modification date.
14
15
16
17
504 Chapter 14: File Transfer Protocols
1
1
Table 14.4 describes a typical YModem protocol transfer.
1
Table 14.4: Description of a typical YModem protocol transfer
Transmitter
Receiver
<---
<SOH><0><255><file info><crc>
<STX><1><254><1024 data bytes><crc>
<ACK>
<---
‘C’
<STX><1><254><1024 data bytes><crc>
4
--->
<ACK>
--->
6
<ACK>
--->
<---
<SOH><0><255><file info><crc>
3
<---
<--<EOT>
‘C’
--->
<--<STX><2><253><1024 data bytes><crc>
2
7
‘C’
--->
8
<---
<ACK>
<---
‘C’
9
--->
<---
<EOT>
--->
<SOH><0><255><128 zeros><crc>
--->
<---
<ACK>
10
11
<ACK>
As with the Xmodem protocols, the Ymodem protocol starts when the receiver sends a
handshake character (‘C’) to the transmitter. The transmitter responds with a properly
formatted block zero. The receiver acknowledges this with an <ACK> and then starts a
normal Xmodem CRC protocol by issuing another ‘C’ handshake character.
12
13
14
15
16
17
Ymodem 505
1
1
1
2
3
4
Ymodem extensions
The Ymodem specification permits Ymodem to use a combination of 128 and 1024 byte
blocks. Most Ymodem protocols start with 1024 byte blocks and drop back to 128 byte
blocks only if repeated errors are detected. Once the block size is reduced to 128 bytes, it is
never stepped back up to 1024.
Like Xmodem, Ymodem also offers a streaming extension called Ymodem G. This is similar
in performance (and drawbacks) to Xmodem 1KG, but like Ymodem itself offers the
advantages of batch transfers and file information.
5
6
7
8
9
10
11
12
13
14
15
16
17
506 Chapter 14: File Transfer Protocols
1
1
Zmodem
1
Of all the protocols supported by Async Professional, Zmodem offers the best overall mix of
speed, features, and tolerance for errors. The Zmodem protocol has many options and
clearly was meant to have lots of room for growth. The Async Professional implementation
of Zmodem does not cover the entire protocol specification but it does implement the
features most likely to be required by your application. It should generally be your protocol
of choice.
2
Zmodem was developed for the public domain by Chuck Forsberg under contract to
Telenet. The original purpose was to provide a durable protocol with strong error recovery
features and good performance over a variety of network types (switched, satellite, etc.). It
has generally achieved these design goals.
What follows is a simplified explanation of Zmodem that provides more than enough
information to use it with Async Professional. Refer to the ZMODEM.DOC file of
PROTDOC.LZH for further details.
Zmodem borrows some concepts from Xmodem, Ymodem, and Kermit but is really a
completely new protocol. Instead of adopting the simple block structure of Xmodem and
Ymodem, Zmodem employs headers, data subpackets, and frames. A header contains a
header identifier, a type byte, four information bytes, and some block check bytes. A data
subpacket contains up to 1024 data bytes, a data subpacket type identifier, and some block
check bytes. A frame consists of one header and zero or more data subpackets.
Due to the complexity and variety of the Zmodem header and data subpacket formats, they
are not all detailed here. Instead, Table 14.5 provides a high level look at a sample Zmodem
file transfer.
Table 14.5: Description of a typical ZModem protocol transfer
Sender
Receiver
--->
Start marker for automated transfers.
ZrQinit
--->
Request for receiver’s information.
ZFile
ZrInit
--->
<---
Receiver answers with its options.
6
7
8
9
10
11
13
14
Transmitter sends file information.
ZrPos
Receiver sets the starting filepos.
ZData
--->
Transmitter says file data to follow.
data subpacket
--->
Transmitter sends a data subpacket.
...
4
12
Explanation
‘rz’<cr>
<---
3
15
16
Continues until all data is sent.
17
Zmodem 507
1
1
1
2
Table 14.5: Description of a typical ZModem protocol transfer (continued)
Sender
ZEof
Receiver
--->
<---
3
ZFin
4
5
6
7
8
‘OO’
Transmitter indicates end-of-file.
ZrInit
--->
<---
Transmitter indicates no more files.
ZFin
--->
Receiver acknowledges.
Transmitter signs off.
The ZXxx tags are the header types that the two computers trade back and forth as they
decide what is to be done. You don’t need to understand them unless you find yourself trying
to decipher a Zmodem transfer using the Async Professional tracing facility. To do so, you’ll
likely need to consult the ZMODEM.DOC file and/or the Async Professional source code.
In most cases all data in the file is sent in one ZData frame (the ZData header followed by as
many data subpackets as required). The receiver doesn’t have to acknowledge any of the
blocks unless the transmitter specifically asks for it. The Zmodem protocol as implemented
by Async Professional never asks for an acknowledgement; however, it respects such
requests from the transmitter.
Typically, once a file transfer is underway, the receiver interrupts the transmitter only if it
receives a bad block as determined by comparing block check values. An error is reported
by sending a ZrPos header, telling the transmitter where in the file to start retransmitting.
10
The protocol can be canceled at any time if either side sends five <CAN> characters (^X).
11
Control character escaping
13
14
Zmodem escapes certain control characters. Escaping means that characters are
transformed before being transmitted to prevent certain binary data characters, such as
<XON> and <CAN>, from being interpreted as data link control characters.
Escaping isn’t something you need to enable or disable because it’s always on. It is mentioned
here because escaping is what permits you to use software flow control with Zmodem. That
isn’t possible with the Xmodem/Ymodem family.
15
16
17
508 Chapter 14: File Transfer Protocols
1
Receiver says ready for next file.
9
12
1
Explanation
Zmodem always escapes the following characters:
<DLE>
<XON>
<XOFF>
<CAN>
<DLE*>
<XON*>
<XOFF*>
Data link escape character (10h, ^P)
XOn character (11h, ^Q)
XOff character (13h, ^S)
Zmodem escape character (18h, ^X)
Data link escape character with high bit set (90h)
XOn character with high bit set (91h)
XOff character with high bit set (93h)
1
2
3
4
Zmodem escapes all control characters when requested to by the remote protocol.
Protocol options
While the Zmodem specification describes all sorts of features, not all Zmodem
implementations are expected to support all of the features. One of the first things that
happens in a Zmodem protocol is that the receiver tells the transmitter what features it
supports. The transmitter might modify its standard behavior to accommodate the
receiver’s support (or lack of support) for a particular option.
6
7
Since this process is handled automatically, you generally don’t need to worry about it. For
your information, the protocol options that Async Professional Zmodem provides and
doesn’t provide are listed here.
8
Async Professional supports the following Zmodem protocol options:
9
• True full duplex for data and control channels.
10
• Receiving data during disk I/O.
• Sending a break signal.
11
• Using 32 bit CRCs.
12
• Escaping all control characters.
Async Professional does not support the following protocol options:
13
• Encryption.
• LZ data compression.
14
• Escaping the 8th bit.
15
• End-of-line conversion for Unix newline characters.
16
• Sparse files.
17
Zmodem 509
1
1
1
2
3
4
5
6
Transfer resume
The Zmodem specification describes an option called recover/resume. This option is
requested by the transmitter when it wants to resume a previously interrupted file transfer.
When the receiver sees the request for this option, it compares the incoming file name with
the files in its destination directory. If the incoming file already exists and is smaller than the
one being transmitted, the receiver assumes that the transmitter wants to transfer only the
remaining portion of the file.
When this condition exists, the receiver opens the existing file and moves the file pointer to
the end of the file. It then tells the transmitter to move its file pointer to the same point in its
copy of the file. The transmitter starts sending data from that point, which resumes the
transfer from where it was interrupted.
This option can also be used to append new data to a remote copy of a file.
In either case, you use this option as follows:
7
ApdProtocol.FileMask := 'BIGFILE';
ApdProtocol.ZmodemRecover := True;
ApdProtocol.StartTransmit;
8
File management options
9
10
11
12
13
14
15
16
Zmodem has a variety of file management options built into it. These are simple rules that
tell Zmodem whether or not to accept a file. Table 14.6 shows the possible options.
Table 14.6: ZModem file management options
Option Code
Explanation
zfoWriteNewerLonger
Transfer if new, newer, or longer.
zfoWriteCrc
Not supported, interpreted same as zfWriteNewer.
zfoWriteAppend
Transfer if new, append if existing.
zfoWriteClobber
Transfer always.
zfoWriteNewer
Transfer if new or newer.
zfoWriteDifferent
Transfer if new or different dates or sizes.
zfoWriteProtect
Transfer only if new.
The zfoWriteCrc option, which requests that a file be transferred only if its CRC is different
from the remote copy’s, is not supported. When this option is requested, it is treated the
same as the zfoWriteNewer option.
17
510 Chapter 14: File Transfer Protocols
1
1
The file management options are always requested by the transmitter. To use them, assign a
value to the ZmodemFileOption property before calling StartTransmit. The default
behavior is zfoWriteNewer. For example, to transmit all files regardless of whether such files
already exist on the remote machine, make the following assignment:
1
2
ApdProtocol.ZmodemFileOption := zfoWriteClobber;
Even though the transmitter sets the file management options, Async Professional allows the
receiver to change them. For example, suppose the transmitter has requested
zfoWriteClobber but you want to accept only newer files. In this case you would set the
ZmodemOptionOverride property to True before calling StartReceive:
3
4
ApdProtocol.ZmodemOptionOverride := True;
ApdProtocol.ZmodemFileOption := zfoWriteNewer;
Setting this property to True tells Zmodem to ignore the file management options requested
by the transmitter and to use zfoWriteNewer instead.
6
Another file management property called ZmodemSkipNoFile is available. Set this property
to True to force the receiver to skip any incoming file that doesn’t already exist in the
destination directory.
7
Whatever file management rules are in effect, the receiver applies them and either accepts
each file or rejects it. If the file is accepted, the file transfer proceeds normally. If the file is
rejected, the receiver sends a ZSkip frame to the transmitter, which stops sending the
current file and moves on to the next one in its list.
8
9
Don’t forget that you can implement your own file management rules with an
OnProtocolAccept event handler.
10
Automatic block size control
11
The Zmodem protocol decreases or increases the number of bytes transmitted per block in
response to retransmission requests, usually due to poor line conditions or random line
errors. The rationale is that small blocks can transmit more frequently without errors, since
there’s less time for a small block to be hit by line noise. And, even if a small block is
corrupted by noise, it is faster to retransmit than a large block.
The protocol employs the following logic to control the block size. If the transmitter receives
an unsolicited request from the receiver to resend data, it reduces the block size from 1024 to
512. If the transmitter receives another request to resend, it reduces the block size from 512
to 256. It never reduces the block size below 256 bytes. Conversely, the transmitter raises the
block size immediately back to 1024 bytes when it sends four blocks in a row without
receiving any requests to resend.
12
13
14
15
16
17
Zmodem 511
1
1
1
2
Similar logic is employed with 8K Zmodem, which uses 8192 byte blocks by default. The
block size is halved for each retransmission request received, down to a minimum of 256
bytes. The block size is increased to 8K bytes in a single step after four blocks are
transmitted without any requests to resend.
3
Block size control is automatic and cannot be disabled. While this behavior is not
documented in the public domain Zmodem specification, it is the process followed by the
popular DSZ program and is acceptable to any common Zmodem implementation.
4
Large block support
5
6
7
8
9
Async Professional Zmodem also includes support for 8K byte blocks. This behavior is
outside the public domain specification and was added largely for programmers who need
to transfer files to or from several popular BBS and FIDONet mailer programs. Since large
blocks are not supported by all common Zmodem implementations, their use is not
automatic—you must specifically enable them before starting a file transfer by setting the
Zmodem8K property to True.
The output buffer size of the port object used by the protocol window must also be large
enough to support the 8K option. The output buffer must be capable of holding an entire 8K
block with escaping, which in the worst case can double the size of the block. Hence, the
output buffer must be at least 2*8192+30, or 16414, bytes. (See “Buffer sizes” on page 485
for more information.)
10
11
12
13
14
15
16
17
512 Chapter 14: File Transfer Protocols
1
1
Kermit
1
The Kermit protocol was developed to allow file transfers in environments that other
protocols can’t handle. Such environments include links that only pass 7 data bits, links that
can’t handle control characters, computer systems that can’t handle large blocks, and diverse
other links such as those between a PC and a mainframe.
2
Kermit is a public domain protocol that was developed at Columbia University. (The name
refers to Kermit the Frog, from The Muppet Show.) What follows is a simplified explanation
of Kermit that provides more than enough information to use it with Async Professional.
For additional details, get the Kermit protocol specification from Columbia University,
Kermit Distribution, Department OP, 612 West 115th Street, New York, NY 10025.
3
4
6
Character quoting
Character quoting means pretty much the same thing that escaping means in Zmodem. The
character is replaced by a quote character and a modified form of the original character. The
quote character tells the receiver how to convert the modified character back to its original
value. Quoting ensures that certain binary characters are never put into the data stream
where they could be misinterpreted by a modem or another part of the serial link.
Although Zmodem transforms only a few critical characters such as <XON> and <XOFF>,
Kermit quotes nearly all characters. This is one of the features that permits Kermit to run in
nearly any environment. When quoting is finished, a Kermit data packet consists almost
entirely of printable ASCII characters. The only exceptions are an <SOH> character at the
start of each packet and a <CR> at the end.
Kermit quotes control characters by replacing them with a quote character and a modified
version of the control character. For example, ^A becomes ‘#A’ where ‘#’ is the quote
character. The process of converting ^A to ‘A’ is called “Ctl” and it works like this:
7
8
9
10
11
12
Ctl(x) = x xor 40h;
13
This operation is its own inverse, that is, Ctl(Ctl(x)) = x.
Kermit also quotes characters with their eighth bit set, which allows it to transmit 8 bit data
over 7 bit data links. The quote character in this case is ‘&’ and the quoted data character is
obtained simply by stripping the high bit. For example, the quoted version of character $C1
(‘A’ with its high bit set) is ‘&A’.
Binary numbers in Kermit packet headers and in repeated character strings are also
transformed to assure that they are printable characters. This is achieved by adding 32 to
each number before it is transmitted and subtracting 32 after it is received. In Kermit
parlance, these operations are known as “ToChar” and “UnChar.”
14
15
16
17
Kermit 513
1
1
1
2
3
Kermit has a simple built-in data compression mechanism called run length encoding.
When it sees a long string of repeated characters, it compresses the string into a quote
character, a length byte, and the repeated character. Obviously, there must be at least 4
repeated characters before there is any compression. The quote character for run length
encoding is ‘~’. Hence, the string “AAAAA” becomes “~%A”, where ‘%’ is equivalent to a
binary 5 after the “ToChar” operation.
Kermit packets
4
Figure 14.5 shows the general format of a Kermit packet.
5
<SOH>
<len>
<seq>
<type>
<up to 91 data bytes>
<check>
<term>
6
7
8
9
10
11
12
Figure 14.5: The format for a typical Kermit packet.
The <SOH> character, also called the mark field, indicates the start of a Kermit packet.
The length byte specifies the number of bytes that follow. Since it must be transmitted as a
printable 7 bit character the binary maximum value is 94, which means that the maximum
length of a normal Kermit packet is 96 bytes including the <SOH> and the <length> field.
The <seq> byte is a packet sequence number in the range of 0 to 63. After 63 it cycles
back to 0.
The <type> byte describes the various Kermit packet types, which are analogous to the
Zmodem frame types.
The data field contains up to 91 bytes including all quote characters. The number of actual
data bytes could be considerably less, particularly if binary data is being transmitted.
13
The standard Kermit <check> field is a single-byte checksum. Kermit offers two optional
block check methods called two-byte checksum and three-byte CRC. See the
BlockCheckMethod property (page 530) for more information.
14
The <term> character is the packet terminator which equals carriage return (ASCII 13) by
default. You will probably never need to change the terminator.
15
16
17
514 Chapter 14: File Transfer Protocols
1
1
Table 14.7 describes a typical Kermit protocol transfer.
1
Table 14.7: A description of a typical Kermit protocol transfer
Transmitter
KSendInit
Receiver
--->
<---
KFile
KData
KAck
KAck
KAck
Receiver acknowledges data packet.
KAck
Receiver acknowledges data packet.
Continues until all data sent.
--->
<---
KBreak
4
Receiver acknowledges filename.
Transmitter sends data packet.
...
KEndoffile
KAck
6
7
Transmitter says end of file.
--->
<---
3
Transmitter sends data packet.
--->
<---
Receiver answers with its options.
Transmitter sends filename.
--->
<---
KData
Transmitter sends its options.
--->
<---
2
Explanation
Receiver acknowledges and closes file.
8
Transmitter says end of protocol.
KAck
Receiver acknowledges end of protocol.
The KXxx tags are the packet types that the two computers exchange as they decide what is
to be done. You don’t need to understand them unless you find yourself trying to decipher a
Kermit transfer using the Async Professional tracing facility. To do so, you’ll likely need to
consult the Kermit specification file and/or the Async Professional source code.
Kermit options
Like Zmodem, Kermit offers a variety of options. An implementation of Kermit is not
required to support all options. Hence, one of the first things that happens in a Kermit
protocol is that the two sides exchange their desired options and use the lowest common
denominator of the two sets.
9
10
11
12
13
14
15
16
17
Kermit 515
1
1
1
2
3
4
5
6
Table 14.8 shows the Kermit options that Async Professional supports and the default values
each uses. The entries in the first column are TApdProtocol property names that can be used
to adjust each option.
Table 14.8: Kermit property options and default values
Property
Default
Explanation
KermitMaxLen
80 bytes
Maximum length of the data field.
KermitTimeoutSecs
5 seconds
Maximum timeout between characters.
KermitPadCount
0 bytes
No pad characters before packets.
KermitPadCharacter
‘ ’
Space character used for padding.
KermitTerminator
<CR>
Packet terminator is a carriage return.
KermitCtlPrefix
‘#’
Control character prefix is ‘#’.
KermitHighbitPrefix
‘Y’
Honor 8-bit quoting but don't require
it.
BlockCheckMethod
‘1’
Use a 1 byte checksum.
KermitRepeatPrefix
‘~’
Repeat prefix is ‘~’.
KermitMaxWindows
0
No sliding windows.
7
8
9
10
11
12
13
14
15
16
KermitMaxLen is the maximum number of bytes you want Kermit to include in one packet.
The normal maximum value is 94; the default value is 80 as suggested by the Kermit
Protocol Manual. If KermitMaxLen exceeds 94, the Kermit “long packets” feature is enabled.
The absolute maximum value is 1024.
KermitTimeoutSecs is the amount of time, in seconds, that a Kermit transmitter will wait for
an acknowledgement or a Kermit receiver will wait for the next byte to be received. If more
than KermitTimeoutSecs seconds elapse without receiving anything, Kermit assumes an
error occurred and resends.
KermitPadCount and KermitPadCharacter describe padding that can be added at the front
of all Kermit packets. The only reason for padding is if the remote machine needs a delay
between sending a packet and receiving a response. In this case, you can specify enough
padding characters to generate the required delay. Generally, though, padding is
unnecessary. The Kermit protocol as implemented by Async Professional automatically
honors a remote’s request for padding.
KermitTerminator is the character that follows the check field in a packet. Although all
Kermit packets have a terminator, it is used only by systems that need an end-of-line
character before they can start processing input.
17
516 Chapter 14: File Transfer Protocols
1
1
KermitCtlPrefix is the control character prefix that Kermit uses when performing “Ctl”
quoting to transform control characters into printable ASCII characters. Generally you
won’t need to change this prefix.
KermitHighbitPrefix specifies how Kermit transforms high-bit characters into characters
without the high-bit set. Generally you won’t need to change this setting. See the property
description for more information.
BlockCheckMethod specifies the type of block checking Kermit should perform. ‘1’
corresponds to the bcmChecksum value of the TBlockCheckMethod type, and it means that
Kermit should use a single-byte checksum. All Kermit implementations are guaranteed to
support this form of block checking. bcmChecksum2 means that Kermit should use a twobyte checksum, which offers only slightly more protection than the single-byte checksum.
bcmCrcK means that Kermit should use a three-byte CRC. This is the preferred block check
method because it offers the highest level of error detection. Unfortunately, not all Kermit
implementations support the non-default block check methods. If the remote computer
doesn’t support the block check method you request, both sides drop back to the single-byte
checksum.
1
2
3
4
6
7
KermitRepeatPrefix is the repeated-character prefix that Kermit uses when compressing
long strings of repeated characters. Generally you won’t need to change this prefix.
8
KermitMaxWindows is the number of sliding windows requested. Setting this to a value
between 1 and 27 (the maximum allowed) enables sliding windows support.
9
The two sides of a Kermit protocol automatically negotiate which options to use, so no
intervention is required by your program. If you wish to change the default options, use
these properties.
10
Async Professional does not provide Kermit server functions and does not support file
attribute packets.
11
Long packets
12
Async Professional includes support for long packets, which is an extension to standard
Kermit that permits data packets of up to 1024 bytes. Long packets can substantially
improve protocol throughput on clean connections that have small turnaround delays. Long
packet support is turned off by default and must be enabled by setting KermitMaxLen to a
value between 95 and 1024. Most other Kermit implementations also disable this option by
default.
13
Although the specification allows for packets up to 9024 bytes, Async Professional limits
long packets to 1024 bytes. Packets longer than 1024 bytes do not appreciably increase
throughput, but they dramatically increase retransmission time when a line error occurs.
14
15
16
17
Kermit 517
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
The specification also recommends the use of the higher-order checksums with long
packets, but does not require it. Async Professional defaults to 2 byte checksums when long
packet support is enabled, but drops back to 1 byte checksums if requested to do so by the
remote Kermit.
Sliding Windows Control
Async Professional includes supports for the Kermit extension known as Sliding Windows
Control, also called “SuperKermit.” Sliding Windows Control (SWC) provides a “sendahead” facility that dramatically improves throughput when turnaround delays tend to be
large, as when using satellite links.
Send-ahead means that the transmitter sends many blocks without waiting for an
acknowledgement for each block. The transmitter collects acknowledgements when they
eventually arrive and marks the previously transmitted blocks as acknowledged. This
reduces turnaround delay (the time it takes the receiver to send an acknowledgement) to
zero.
SWC operates by keeping a circular table of transmitted packets. The maximum number of
packets in this table is called the window size, which is a number between 0 (no sliding
window support) and 31. If the transmitter and receiver specify different window sizes, the
smaller of the two is used. Async Professional’s Kermit actually limits the maximum number
of windows to 27 to avoid encountering a bug in the popular program MSKERMIT.
Sliding window support is off by default. It is enabled by setting the KermitMaxWindows
property to a non-zero value.
On the sender’s side, each transmitted packet is added to the table. When an
acknowledgement is eventually received for a packet, its entry in the table is freed. If the
table fills, the transmitter does not send more packets until it receives acknowledgements for
one or more existing packets.
On the receiver’s side, each received packet is added to the table and remains there until the
table is full. Then the oldest packet is written to disk. When errors are detected, the receiver
sends a <NAK> for each missed packet, starting past the last known good packet and
continuing up to the most recently received packet.
It is possible to enable long packets and SWC simultaneously, but memory consumption
rises dramatically from the single 80 byte buffer normally used by Kermit. In the worst case
you could have 27 windows of 1024 bytes each, adding up to 27648 bytes.
16
17
518 Chapter 14: File Transfer Protocols
1
1
ASCII
1
The term ASCII protocol is a bit of a misnomer, because in an ASCII transfer neither side of
the link is following well-documented rules. An ASCII protocol is really just a convenient
way of transmitting a text file.
2
A typical use for the ASCII protocol is when you need to transfer a text file to a
minicomputer that doesn’t have any protocols available. One way of accomplishing this is to
run an Async Professional program that supports a terminal window and the ASCII
protocol, such as the TCom demo program. You connect to the minicomputer, navigate to
the minicomputer’s editor, and open up a new text file. Then you start an ASCII protocol
transmit of the file you need to transfer. The minicomputer’s editor sees this as keystroke
input to the editor. You finish the transfer by saving the editor’s file.
The ASCII protocol provides options for tailoring such transfers to the remote machine’s
speed, which might necessitate delays between transmitted characters and lines. For
example, when transmitting a file into a remote computer’s editor, you might need to use
delays to avoid overflowing the editor’s keystroke buffer.
It is difficult for the receiver to know when an ASCII transfer is over because there is no
agreed-upon method for indicating termination. The ASCII protocol terminates on any of
three conditions: when it receives a ^Z character, when it times out waiting for more data, or
when the user aborts the protocol and the application calls CancelProtocol. When any of
these conditions is detected, the file is saved and the protocol ends.
3
4
6
7
8
9
10
End-of-line translations
Computer systems sometimes use different character sequences to terminate each line of a
text file. Most PC software stores both a carriage return <CR> and a line feed <LF> at the
end of each line. Other systems store only <LF> or only <CR> at the end of each line.
11
12
13
14
15
16
17
ASCII 519
1
1
1
2
3
4
The ASCII protocol provides a number of options for translating from one end-of-line
sequence to another, both when transmitting and when receiving. When performing these
translations, the <CR> and <LF> characters are treated separately, based on the values
assigned to the AsciiCRTranslation and AsciiLFTranslation properties. Table 14.9 shows the
enumerated values used to control the behavior.
Table 14.9: ACII protocol enumerated property values
Value
Explanation
aetNone
The character is not to be modified (the default).
aetStrip
The character is to be stripped from the data stream.
5
aetAddCRBefore
A <CR> is to be inserted before each <LF>. This can be
specified only for the AsciiLFTranslation property.
6
aetAddLFAfter
An <LF> is to be added after each <CR>. This can be
specified only for the AsciiCRTranslation property.
7
8
9
10
11
12
13
14
15
16
17
520 Chapter 14: File Transfer Protocols
1
1
FTP
1
Async Professional provides two File Transfer Protocol (FTP) components that make it easy
to implement FTP client support in your application.
2
The TApdFtpClient component takes care of the FTP protocol details and presents a
friendly interface for transferring files to and from an FTP server. In addition, the
component also provides a set of standard functions for manipulating files and directories at
the FTP server.
3
4
The TApdFtpLog component automates the process of logging an FTP client-server dialog
for auditing FTP activities.
Overview of FTP
6
File Transfer Protocol (FTP) is used to transfer files from one location on the Internet to
another. An FTP client establishes a connection (called the control connection) to an FTP
server at a well-known port number (usually 21). The control connection is used for a
command-response dialog between the client and server. The client issues an FTP
command which consists of an ASCII string containing a mnemonic command followed by
any parameters required for the command. The server then responds with a reply consisting
of an ASCII string containing a three digit reply code followed by some text. During a file
transfer, a separate data connection is established to transfer the file data. When the transfer
is complete, the data connection is closed.
When an FTP client establishes a control connection with an FTP server, the server will
respond with a reply code that indicates that the user login procedure can commence. The
login procedure consists of sending commands containing the user’s ID name, password,
and (possibly) account information as parameters. An FTP server requires authenticated
login information before giving a user access to files and directories, however many servers
allow a user to login “anonymously” for restricted access. Users log in anonymously using
“ANONYMOUS” for their user ID name and their e-mail address for their password.
7
8
9
10
11
12
13
14
15
16
17
FTP 521
1
1
1
2
FTP error codes
If an FTP server rejects a command from an FTP client for one reason or another, the server
will reply with an error code. Table 14.10 is a list of common FTP errors.
Table 14.10: Common FTP errors
3
4
5
6
7
8
9
10
11
12
Reply Code
Explanation
421
Service not available, closing control connection.
425
Can’t open data connection.
426
Connection closed, transfer aborted.
451
Requested action aborted, local error in processing.
452
Requested action not taken.
500
Syntax error, command unrecognized.
501
Syntax error in parameters or arguments.
502
Command not implemented.
503
Bad sequence of commands.
504
Command not implemented for that parameter.
505
No such file or directory.
506
Usage error.
522
Transfer error bytes written.
530
Error in user login.
550
Requested action not taken due to error.
551
Requested action aborted.
553
Requested action not taken due to system error.
13
14
15
16
17
522 Chapter 14: File Transfer Protocols
1
1
TApdProtocol Component
1
TApdProtocol implements all of the Async Professional file transfer capabilities in one
comprehensive component. General issues associated with using this component are
discussed in the first part of this chapter.
2
Note that certain properties that are described in the following reference section are specific
to a particular protocol type. If a particular property is not supported by the current value of
the ProtocolType property (e.g., the AsciiCharDelay property is not relevant to the
Zmodem protocol), assigning a value to that property stores the new value in a field of the
component, but has no effect until the ProtocolType is changed to the corresponding
protocol. Protocol-specific properties have names that begin with the name of the protocol
itself (e.g., ZmodemOptionOverride, ZmodemSkipNoFile, ZmodemFileOption,
ZmodemRecover, and Zmodem8K).
3
4
6
Example
7
This example shows how to construct and use a protocol component. It includes a terminal
window so you can navigate around an on-line service while you test the program, and a
TApdProtocolStatus component so you can see the progress of the transfer.
8
Create a new project, add the following components, and set the property values as
indicated in Table 14.11.
9
10
Table 14.11: Example components and property values
Component
Property
Value
TApdComPort
ComNumber
<Set as needed for your PC>
11
TApdEmulator
12
TApdTerminal
TApdProtocol
FileMask
EXPROT*.*
TButton
Name
Upload
TButton
Name
Download
13
TApdProtocolStatus
14
15
16
17
TApdProtocol Component 523
1
1
1
2
3
Double-click on the Upload button’s OnClick event handler within the Object Inspector and
modify the generated method to match this:
procedure TForm1.UploadClick(Sender : TObject);
begin
ApdProtocol1.StartTransmit;
end;
4
This method starts a Zmodem background protocol transmit session for all of the files
matching the mask “EXPROT*.*”. (Zmodem is the default protocol type for TApdProtocol
instances.)
5
Double-click on the Download button’s OnClick event handler within the Object Inspector
and modify the generated method to match this:
6
7
8
9
10
11
12
procedure TForm1.DownloadClick(Sender : TObject);
begin
ApdProtocol1.StartReceive;
end;
This method starts a Zmodem background session to receive whatever files the transmitter
sends.
The form includes a TApdProtocolStatus component, which is automatically displayed by
the protocol and periodically updated to show the progress of the file transfer.
This example is in the EXPROT project in the \ASYNCPRO\EXAMPLES directory.
Hierarchy
TComponent (VCL)
! TApdBaseComponent (OOMisc) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
TApdCustomProtocol (AdProtcl)
TApdProtocol (AdProtcl)
13
14
15
16
17
524 Chapter 14: File Transfer Protocols
1
1
Properties
1
AbortNoCarrier
FileName
ProtocolError
ActualBPS
FinishWait
ProtocolLog
AsciiCharDelay
HandshakeRetry
ProtocolStatus
AsciiCRTranslation
HandshakeWait
ProtocolType
AsciiEOFTimeout
HonorDirectory
RTSLowForWrite
AsciiEOLChar
IncludeDirectory
StatusDisplay
AsciiLFTranslation
InitialPosition
StatusInterval
AsciiLineDelay
InProgress
TotalErrors
AsciiSuppressCtrlZ
KermitCtlPrefix
TransmitTimeout
Batch
KermitHighbitPrefix
TurnDelay
BlockCheckMethod
KermitLongBlocks
BlockErrors
KermitMaxLen
BlockLength
KermitMaxWindows
WriteFailAction
BlockNumber
KermitPadCharacter
XYmodemBlockWait
BytesRemaining
KermitPadCount
Zmodem8K
BytesTransferred
KermitRepeatPrefix
ZmodemFileOption
ComPort
KermitSWCTurnDelay
ZmodemFinishRetry
DestinationDirectory
KermitTerminator
ZmodemOptionOverride
ElapsedTicks
KermitTimeoutSecs
ZmodemRecover
FileDate
KermitWindowsTotal
ZmodemSkipNoFile
FileLength
KermitWindowsUsed
FileMask
Overhead
UpcaseFileNames
2
3
4
6
! Version
7
8
9
10
11
Methods
12
CancelProtocol
StartReceive
EstimateTransferSecs
StartTransmit
StatusMsg
13
Events
14
OnProtocolAccept
OnProtocolFinish
OnProtocolNextFile
OnProtocolError
OnProtocolLog
OnProtocolStatus
15
16
17
TApdProtocol Component 525
1
1
1
Reference Section
AbortNoCarrier
property
2
property AbortNoCarrier : Boolean
3
Default: False
! Determines whether the protocol is canceled automatically when the DCD modem signal
4
5
6
7
8
9
10
drops.
Using the AbortNoCarrier property is better than checking DCD and calling
CancelProtocol in your own code. When you do this, the protocol engine sends a cancel
sequence to the remote computer. If hardware flow control is enabled and the modem has
lowered the DSR or CTS signals as well as DCD, the protocol waits several seconds before
deciding it can’t send the cancel command, leading to an unnecessary delay for the
application. The AbortNoCarrier property prevents the protocol engine from sending the
cancel sequence, so the protocol stops immediately.
Note that when transferring through Winsock the DCD signal is not present (Winsock does
not contain the concept of a DCD line). The TApdWinsockPort artificially raises and lowers
the DCD property according to the connection state. The DCD property is therefore valid
for testing connection states, and the AbortNoCarrier functionality will work as expected.
See also: CancelProtocol
ActualBPS
run-time property
property ActualBPS : LongInt
11
12
13
14
15
! Determines the data transfer rate used by EstimateTransferSecs.
This property can be used to set a bit per second (bps) rate that differs from the associated
comport component’s baud rate. The bps rate is used only by EstimateTransferSecs to
compute transfer times. The actual bps differs from the port baud rate only in cases like the
following: Two machines are communicating via MNP or V.32 modems at 9600 bps; both
modems are using built-in data compression facilities to increase the effective bps rate
(perhaps to 11000 bps); the machines are communicating with their modems at a rate of
19200 baud to ensure that the modems don’t waste time waiting for data to send; and the
machines are using hardware flow control to pace the flow of data between the modems and
the machines.
16
17
526 Chapter 14: File Transfer Protocols
1
1
Without setting ActualBPS, the protocol would base transfer rate calculations on a bps rate
of 19200, the port baud rate. You should set ActualBPS to 9600, the actual connection speed.
Even this is somewhat inaccurate because it doesn’t take into account the improvements due
to data compression, which are difficult to predict.
1
2
See also: EstimateTransferSecs
AsciiCharDelay
property
property AsciiCharDelay : Word
3
4
Default: 0
! Determines the number of milliseconds to delay between characters during an ASCII file
transfer.
The default delay of zero should be retained whenever possible to maximize performance.
However, if ASCII data is being fed directly into an application such as a text editor, it might
be necessary to insert delays to allow the application time to process the data.
The following example sets the inter-character delay to 2 milliseconds and the inter-line
delay to 50 milliseconds:
ApdProtocol1.AsciiCharDelay := 2;
ApdProtocol1.AsciiLineDelay := 50;
6
7
8
9
See also: AsciiLineDelay
AsciiCRTranslation
property
property AsciiCRTranslation : TAsciiEOLTranslation
TAsciiEOLTranslation = (
aetNone, aetStrip, aetAddCRBefore, aetAddLFAfter);
10
11
12
Default: aetNone
! Determines the end-of-line translation mode for carriage returns.
13
Acceptable values to assign to this property are as follows:
14
Value
Description
aetNone
Do not modify the character.
aetStrip
Strip the character from the data stream.
aetAddLFAfter
Add an <LF> after each <CR>.
aetAddCRBefore does not apply to AsciiCRTranslation, so it is treated as aetNone.
15
16
17
TApdProtocol Component 527
1
1
1
2
3
4
5
6
7
8
9
The following example causes all <LF> characters to be stripped while <CR> characters are
transmitted:
ApdProtocol1.ProtocolType := ptAscii;
ApdProtocol1.AsciiCRTranslation := aetNone;
ApdProtocol1.AsciiLFTranslation := aetStrip;
ApdProtocol1.StartTransmit;
See also: AsciiEOLChar, AsciiLFTranslation
AsciiEOFTimeout
property
property AsciiEOFTimeout : Word
Default: 364
! Determines the number of ticks before an ASCII transfer is automatically terminated.
Because most text files are terminated by a ^Z character (ASCII 26), the ASCII protocol
closes the file and ends the protocol when it finds a ^Z. If the received file isn’t terminated by
a ^Z, the ASCII protocol determines the file was completely received after a specified
number of ticks elapse without receiving any new data. The default of 20 seconds can be
changed by assigning a new tick value to this property.
AsciiEOLChar
property
property AsciiEOLChar : Char
10
11
12
Default: ^M (ASCII 13)
! Determines the character that triggers an inter-line delay.
After an ASCII file transmit sends the character specified by this property, it pauses for the
number of milliseconds specified by the AsciiLineDelay property.
13
Note that this character is not involved in on-the-fly translation of end-of-line characters
read from or written to an ASCII file; that translation is controlled by the
AsciiCRTranslation and AsciiLFTranslation properties.
14
The default end-of-line character is <CR> or ^M. If you are transmitting Unix files, which
use <LF> or ^J for the end-of-line marker, you should set AsciiEOLChar to ^J.
See also: AsciiLineDelay
15
16
17
528 Chapter 14: File Transfer Protocols
1
1
AsciiLFTranslation
property
1
property AsciiLFTranslation : TAsciiEOLTranslation
!
TAsciiEOLTranslation = (
aetNone, aetStrip, aetAddCRBefore, aetAddLFAfter);
2
Default: aetNone
3
Determines the end-of-line translation mode for line feeds.
4
Acceptable values to assign to this property are as follows:
Value
Description
aetNone
Do not modify the character.
aetStrip
Strip the character from the data stream.
aetAddCRBefore
Insert a <CR> before each <LF>.
6
aetAddLFAfter does not apply to AsciiLFTranslation, so it is treated as aetNone.
7
See also: AsciiCRTranslation, AsciiEOLChar
AsciiLineDelay
property
property AsciiLineDelay : Word
8
9
Default: 0
! Determines the number of milliseconds to delay between lines during an ASCII file transfer.
10
The default delay of zero should be retained whenever possible to maximize performance.
However, if ASCII data is being fed directly into an application such as a text editor, it might
be necessary to insert delays to allow the application time to process the data.
11
See also: AsciiCharDelay
12
AsciiSuppressCtrlZ
property
13
property AsciiSuppressCtrlZ : Boolean
14
Default: False
! Determines whether an ASCII protocol stops transmitting when it encounters the first ^Z in
the file.
If this property is False, the ASCII protocol transmits all characters in the file, including ^Z
characters. If it is True, it stops before transmitting the first ^Z that it encounters. Generally
you should leave this property set to False because the receiver might use ^Z as an end-ofprotocol indicator, as Async Professional does.
15
16
17
TApdProtocol Component 529
1
1
1
Batch
read-only, run-time property
property Batch : Boolean
2
! Returns True if the current protocol supports batch transfers.
3
Batch transfers are those that allow sending more than one file in the same protocol session.
Batch transfers in Async Professional include: Kermit, Ymodem, and Zmodem.
4
This property is most useful within an OnProtocolStatus event handler. See “Protocol
status” on page 489 for more information.
5
BlockCheckMethod
property
property BlockCheckMethod : TBlockCheckMethod
6
7
8
TBlockCheckMethod = (
bcmNone, bcmChecksum, bcmChecksum2, bcmCrc16, bcmCrc32, bcmCrcK);
! Determines the error checking method used by the protocol.
The default error checking method depends on the protocol. See the section describing each
protocol at the beginning of this chapter for additional information.
The following values can be assigned to the property:
9
10
11
12
13
14
Value
Description
bcmNone
No error checking.
bcmChecksum
Single byte checksum.
bcmChecksum2
Two byte checksum used by Kermit.
bcmCrc16
16-bit CRC.
bcmCrc32
32-bit CRC used by Zmodem.
bcmCrcK
Three byte CRC used by Kermit.
The Xmodem1K, Xmodem1KG, Ymodem, YmodemG, and ASCII protocols provide either
no error checking or a single error checking mode, so they ignore assignments to
BlockCheckMethod.
15
16
17
530 Chapter 14: File Transfer Protocols
1
1
Assigning bcmCrc16 to BlockCheckMethod converts an Xmodem protocol into an
XmodemCrc protocol. Conversely, assigning bcmCheckSum to BlockCheckMethod
converts an XmodemCrc protocol to an Xmodem protocol.
1
The Zmodem protocol accepts only the bcmCrc16 and bcmCrc32 types. The Kermit
protocol accepts only the bcmChecksum, bcmCheckSum2, and bcmCrcK types.
2
No error is generated if an unaccepted type is assigned, but the assignment is ignored. You
should be sure to set the desired ProtocolType before setting a non-default
BlockCheckMethod.
3
4
See also: ProtocolType
BlockErrors
read-only, run-time property
property BlockErrors : Word
6
! The number of errors that have occurred while transferring the current block.
This property is most useful within an OnProtocolStatus event handler. See “Protocol
status” on page 489 for more information.
7
See also: TotalErrors
8
BlockLength
read-only, run-time property
9
property BlockLength : Word
! The number of bytes currently being transferred per block.
10
For some protocols this value remains fixed (e.g., Xmodem always uses 128 byte blocks); for
others it can vary during the transfer process (e.g., Zmodem can vary between 8192 bytes
and 256 bytes depending on options and line conditions).
This property is most useful within an OnProtocolStatus event handler. See “Protocol
status” on page 489 for more information.
12
BlockNumber
13
read-only, run-time property
property BlockNumber : Word
!
11
14
The number of blocks transferred so far.
This is obtained by dividing the number of bytes transferred by the current block length, so
it will change if the block length changes.
15
This property is most useful within an OnProtocolStatus event handler. See “Protocol
status” on page 489 for more information.
16
17
TApdProtocol Component 531
1
1
1
BytesRemaining
read-only, run-time property
property BytesRemaining : LongInt
2
! The number of bytes still to be transferred in the current file.
3
This is computed as the FileLength minus the value of BytesTransferred. When the file size
isn’t known, BytesRemaining returns zero.
4
This property is most useful within an OnProtocolStatus event handler. See “Protocol
status” on page 489 for more information.
See also: BytesTransferred, FileLength
5
6
BytesTransferred
read-only, run-time property
property BytesTransferred : LongInt
! The number of bytes transferred so far in the current file.
7
8
9
10
When transmitting, this number is sometimes only an estimate. The uncertainty comes
from the fact that the protocol window doesn’t know when a particular byte has actually
been transferred. BytesTransferred is the number of bytes the protocol window has
transferred to the output buffer of the communications driver, minus the number of bytes
that the driver reports are currently in the buffer.
Unfortunately, this calculation is still imperfect because it’s impossible to know how much of
the output buffer holds actual file data and how much holds overhead characters needed by
the protocol. Each protocol has a few simple rules it uses to estimate this proportion, which
in practice yield good estimates.
11
This property is most useful within an OnProtocolStatus event handler. See “Protocol
status” on page 489 for more information.
12
See also: BytesRemaining
13
14
15
16
17
532 Chapter 14: File Transfer Protocols
1
1
CancelProtocol
method
1
procedure CancelProtocol;
! Cancels the protocol currently in progress.
2
CancelProtocol cancels the protocol regardless of its current state. If appropriate, a cancel
string is sent to the remote computer. The protocol generates an OnProtocolFinish event
with the error code ecCancelRequested, then cleans up and terminates.
The following example shows how to cancel a protocol from within a protocol status dialog
box:
procedure TStandardDisplay.CancelClick(Sender: TObject);
begin
ApdProtocol1.CancelProtocol;
end;
3
4
6
See also: InProgress, OnProtocolError
ComPort
property
7
8
property ComPort : TApdCustomComPort
! Determines the serial port used by the protocol.
A properly initialized comport component must be assigned to this property before using
the protocol.
9
The comport should almost always be set to use 8 data bits, 1 stop bit, and no parity. It
should have input and output buffers that meet the guidelines described in “Buffer sizes” on
page 485. Most transfer protocols require that some form of flow control be enabled in the
comport component.
10
DestinationDirectory
12
property
property DestinationDirectory : string
11
13
! Determines the directory where received files are stored.
If the value specifies only a drive (e.g., “D:”), files are stored in the current directory on that
drive. If the property is set to an empty string, as it is by default, received files are stored in
the current directory.
See also: FileName
14
15
16
17
TApdProtocol Component 533
1
1
1
ElapsedTicks
read-only, run-time property
property ElapsedTicks : LongInt
2
3
! The time elapsed since the protocol started.
In order to provide accurate character per second transfer rates, the protocol engine doesn’t
start the timer until it receives the first block from the remote computer, or until it sends the
first data block. ElapsedTicks is measured in ticks, which occur at roughly 18.2 per second.
4
This property is most useful within an OnProtocolStatus event handler. See “Protocol
status” on page 489 for more information.
5
See also: EstimateTransferSecs
EstimateTransferSecs
method
6
function EstimateTransferSecs(const Size : LongInt) : LongInt;
7
8
9
10
11
12
13
14
! Returns the amount of time to transfer a file.
You can call EstimateTransferSecs in a status event handler to obtain the approximate
number of seconds required to transfer Size bytes of data. Typically, a status routine calls it
in two places. In the first place, which should generally be executed only one time when the
status routine is first called, it passes the total size of the file to get the total transfer time. In
the second place, which should be executed every time the status routine is called, it passes
the number of bytes remaining to get the transfer time remaining.
EstimateTransferSecs automatically accounts for the baud rate of the port’s connection and
various internal details of the active protocol. The estimated transfer time is also affected by
two approximate overhead factors that are specific to the type of protocol. See the Overhead
and TurnDelay properties for more information about these factors. If the modem data rate
is different from the comport data rate, also see ActualBPS.
To compute the transfer time, EstimateTransferSecs first computes an effective transfer rate
using the following formulas:
ActualCPS
= ActualBPS div 10
Efficiency
= ratio of data bytes to highest possible number of
bytes, calculated as follows:
15
BlockLength
-------------------------------------------------
16
BlockLength + Overhead + ((TurnDelay * ActualCPS)
div 1000)
EffectiveCPS = ActualCPS * Efficiency
17
534 Chapter 14: File Transfer Protocols
1
1
Then the estimated transfer time is Size divided by EffectiveCPS.
The following example calls EstimateTransferSecs in a status routine to get the total and
remaining transfer times:
procedure TForm1.ProtocolStatus(CP : TObject; Options : Word);
var
TotalTime, RemainingTime : LongInt;
begin
with TApdProtocol1(CP) do begin
...
TotalTime := EstimateTransferSecs(FileLength);
RemainingTime := EstimateTransferSecs(BytesRemaining);
....
end;
end;
1
2
3
4
6
See also: ActualBPS, OnProtocolStatus, Overhead, TurnDelay
FileDate
read-only, run-time property
property FileDate : TDateTime
7
8
! Returns the date and time of the file being transferred.
For transmitted files the file timestamp is always known. For received files the timestamp is
known only if the protocol supports this feature and the receiver has received this
information. FileDate is accurate after FileName returns a non-empty string.
9
10
If the timestamp is not known, FileDate returns zero.
This property is most useful within an OnProtocolStatus event handler. See “Protocol
status” on page 489 for more information.
11
See also: FileLength, FileName
12
13
14
15
16
17
TApdProtocol Component 535
1
1
1
FileLength
read-only, run-time property
property FileLength : LongInt
2
3
4
5
6
7
8
9
10
11
12
! Returns the size of the file being transferred.
For transmitted files the file size is always known. For received files the file size is known
only if the protocol supports this feature and the receiver has received this information.
FileLength is known after FileName returns a non-empty string. If the file size is not known,
FileLength returns zero.
This property is most useful within an OnProtocolStatus event handler. See “Protocol
status” on page 489 for more information.
See also: FileDate
FileMask
property FileMask : TFileName
! Determines the file mask to use when transmitting files.
FileMask can specify a single file or can contain DOS wildcards to transmit multiple files
using a batch protocol such as Zmodem. If it does not specify a drive and directory, files are
read from the current directory.
Only a single mask can be used for each transfer. To transfer a group of files that cannot be
described by a single mask, see “NextFile processing” on page 494.
The following example transmits all files with a ZIP extension in the C:\UPLOAD directory:
ApdProtocol1.FileMask := 'C:\UPLOAD\*.ZIP';
ApdProtocol1.StartTransmit;
See also: Batch
FileName
13
14
15
16
! Determines the name of the file currently being received.
This should be considered a read-only property for all protocols except Xmodem and
ASCII, which do not transfer a filename along with the file data. For these two protocols you
must assign a value to FileName before calling StartReceive. For the remaining protocols
supported by Async Professional, you can read the value of FileName within a protocol
status routine to obtain the file name transferred by the protocol.
536 Chapter 14: File Transfer Protocols
1
property
property FileName : string
17
1
property
If FileName does not include drive or path information, the incoming file is stored in the
current directory or the directory specified by DestinationDirectory. If FileName includes
drive and/or path information and HonorDirectory is True, the incoming file is stored in
that directory regardless of whether a value was assigned to DestinationDirectory.
The following example stores a file received via Xmodem to
C:\DOWNLOAD\RECEIVE.TMP:
1
2
3
ApdProtocol1.ProtocolType := ptXmodem;
ApdProtocol1.FileName := 'C:\DOWNLOAD\RECEIVE.TMP';
ApdProtocol1.StartReceive;
4
See also: DestinationDirectory, HonorDirectory
FinishWait
property
6
property FinishWait : Word
Default: 364
! Determines how long the receiver waits for an end-of-transmission signal before timing out.
This property applies only to Xmodem, Ymodem, and Zmodem protocols.
At the end of an Xmodem or Ymodem file transfer the transmitter sends an <EOT> to the
receiver to signal the end of the file and then waits FinishWait ticks (20 seconds by default)
for a response. Normally this provides ample time. However, when Xmodem1KG and
YmodemG are used over links with long propagation times or slow receivers, the default
value might not be enough. Use FinishWait to extend the amount of time that the
transmitter waits before timing out and reporting an error. Note that FinishWait is specified
in ticks.
Similarly, in a Zmodem transfer the transmitter sends a ZFin packet to the receiver to signal
the end of the file and then waits FinishWait ticks to receive an acknowledgement before
timing out.
See also: ZmodemFinishRetry
7
8
9
10
11
12
13
14
15
16
17
TApdProtocol Component 537
1
1
1
HandshakeRetry
property
property HandshakeRetry : Word
2
Default: 10
! Determines the retry count for protocol handshaking.
3
4
This property controls how many times each protocol attempts to detect the initial
handshake from its remote partner. HandshakeRetry applies to all protocols but ASCII,
which does not perform handshaking.
See also: HandshakeWait
5
6
HandshakeWait
property
property HandshakeWait : Word
Default: 182
7
! Determines the wait between retries for protocol handshaking.
8
This property is the number of ticks a protocol waits when a handshake attempt fails before
it tries again. HandshakeWait applies to all protocols but ASCII, which does not perform
handshaking.
9
See also: HandshakeRetry
10
HonorDirectory
property
property HonorDirectory : Boolean
11
12
Default: False
! Determines whether a protocol honors the directory name of a file being received.
13
If HonorDirectory is set to True, a received file is stored in the directory specified by the
transmitter, unless that directory does not already exist, in which case it is stored in the
current directory or the DestinationDirectory. If HonorDirectory is set to False, the
transmitter’s directory is ignored.
14
See also: IncludeDirectory
15
16
17
538 Chapter 14: File Transfer Protocols
1
1
IncludeDirectory
property
1
property IncludeDirectory : Boolean
2
Default: False
! Determines whether the complete pathname is transmitted.
If IncludeDirectory is set to True, the protocol sends the drive and directory along with the
file name of each file it transmits. The receiver might use or ignore this information. If
IncludeDirectory is False, only the file name is transmitted, even if the file is not found in the
current directory.
3
4
See also: HonorDirectory
InitialPosition
read-only, run-time property
6
property InitialPosition : LongInt
! The initial file offset for a resumed transfer.
7
This property applies only to Zmodemprotocols, which support resumed file transfers. For a
transfer from scratch, InitialPosition returns zero. For a resumed transfer, InitialPosition
returns the offset where the transfer was resumed. This offset should be subtracted from
BytesTransferred to obtain the actual number of bytes transferred during the resumed
session.
8
9
This property is most useful within an OnProtocolStatus event handler. See “Protocol
status” on page 489 for more information.
10
The following example shows how to compute the character per second transfer rate in a
protocol status routine. The constant values are used to convert ticks to seconds. Note that
the same expression is valid whether or not the transfer has been resumed.
11
CPS :=
(91*(ApdProtocol.BytesTransferred-ApdProtocol.InitialPosition))
div (5*ApdProtocol.ElapsedTicks);
See also: BytesTransferred
12
13
14
15
16
17
TApdProtocol Component 539
1
1
1
InProgress
read-only, run-time property
property InProgress : Boolean
2
! Returns True if a protocol is currently in progress.
3
A property such as this is important because Async Professional protocols run in the
background. A call to StartTransmit or StartReceive returns immediately to your code.
4
InProgress is True immediately after StartTransmit or StartReceive is called. InProgress is
False immediately before the OnProtocolFinish event is generated.
5
Use InProgress to determine whether a file transfer is already taking place or not before
trying to start another transfer.
See also: OnProtocolFinish
6
7
KermitCtlPrefix
property
property KermitCtlPrefix : Char
Default: ‘#’
8
9
10
11
12
! Determines the character Kermit uses to quote control characters.
See “Character quoting” on page 513 for more information.
See also: KermitHighbitPrefix, KermitRepeatPrefix
KermitHighbitPrefix
property KermitHighbitPrefix : Char
Default: ‘Y’
! Determines the technique Kermit uses to quote characters that have their eighth bit set.
13
The value specified by this property is not always transmitted literally as a quote character. If
it equals ‘Y’, the default, it means that the protocol won’t use high bit quoting unless the
remote requires it, in which case it uses the prefix character requested by the remote.
14
If KermitHighbitPrefix equals ‘&’ or is in the ASCII range 33-62 or 96-126, it indicates that
the protocol requires high bit quoting and that its value is the character used for the prefix.
15
If C equals ‘N’ or any other value not listed here, the protocol won’t use high bit quoting at
all, even if the remote requests it.
16
See “Character quoting” on page 513 for more information.
See also: KermitCtlPrefix, KermitRepeatPrefix
17
540 Chapter 14: File Transfer Protocols
1
1
property
KermitLongBlocks
read-only, run-time property
1
property KermitLongBlocks : Boolean
! Returns True if Kermit long packets are in use.
2
See also: KermitMaxLen
KermitMaxLen
property
3
4
property KermitMaxLen : Word
Default: 80
! Determines the maximum number of bytes in one Kermit packet.
The normal maximum value is 94, but the default value of 80 is suggested by the Kermit
Protocol Manual. If KermitMaxLen is set to a value in the range of 95 to 1024, long packets
are enabled with the specified packet size. As with other Kermit settings, however, long
packets will be used only if the remote partner also supports it.
6
7
See “Kermit options” on page 515 for more information.
8
See also: KermitMaxWindows
KermitMaxWindows
property
9
property KermitMaxWindows : Word
10
Default: 0
! Determines whether Kermit sliding windows control is enabled.
If KermitMaxWindows is set to a value between 1 and 27, sliding windows are enabled with
the specified window count. This allows a Kermit transmitter to send additional packets
without waiting for an acknowledgement from the receiver, thus improving throughput. As
with other Kermit settings, however, sliding windows control will be used only if the remote
partner also supports it.
11
12
13
See “Sliding Windows Control” on page 518 for more information.
14
See also: KermitWindowsTotal, KermitWindowsUsed
15
16
17
TApdProtocol Component 541
1
1
1
KermitPadCharacter
property
property KermitPadCharacter : Char
2
Default: ‘ ’ (ASCII 32)
! Determines the character that Kermit uses to pad the beginning of each packet.
3
4
See “Kermit options” on page 515 for more information.
See also: KermitTerminator
KermitPadCount
5
6
7
8
property
property KermitPadCount : Word
Default: 0
! Determines the number of pad characters that Kermit transmits at the beginning of each
packet.
See “Kermit options” on page 515 for more information.
See also: KermitPadCharacter
KermitRepeatPrefix
property
9
property KermitRepeatPrefix : Char
10
Default: ‘~’
! Determines the prefix that Kermit uses when compressing strings of repeated characters.
11
12
When Kermit sees four or more equal and adjacent characters, it compresses the sequence
into a quote character (KermitRepeatPrefix), a length byte, and the repeated character. The
default quote character rarely needs to be changed.
See “Character quoting” on page 513 for more information.
13
See also: KermitCtlPrefix, KermitHighbitPrefix
14
15
16
17
542 Chapter 14: File Transfer Protocols
1
1
KermitSWCTurnDelay
property
1
property KermitSWCTurnDelay : Word
2
Default: 0
! Determines the turnaround delay used by EstimateTransferSecs when a Kermit sliding
3
windows protocol is in use.
This property is the time in milliseconds for a data block to transit from the sender to the
receiver, for the receiver to send an acknowledgement, and for the acknowledgement to
arrive back at the sender. It is used by the EstimateTransferSecs method to estimate the time
to transfer a given amount of data.
When Kermit sliding windows control is enabled, the transmitter does not generally wait for
acknowledgement of a packet before sending the next one. Hence, an appropriate default is
zero milliseconds.
4
6
EstimateTransferSecs uses the value of the TurnDelay property for Kermit transfers when
sliding windows control is not enabled, and the KermitSWCTurnDelay property when it is
enabled.
7
See also: Overhead, TurnDelay
8
KermitTerminator
property
9
property KermitTerminator : Char
10
Default: ^M (ASCII 13)
! Determines the character used to terminate a Kermit data packet.
This character is used only by Kermit hosts that cannot start processing a data line until a
terminating character is received.
See “Kermit options” on page 515 for more information.
See also: KermitPadCharacter
11
12
13
14
15
16
17
TApdProtocol Component 543
1
1
1
KermitTimeoutSecs
property
property KermitTimeoutSecs : Word
2
Default: 5
! Determines how long Kermit waits for the next expected byte.
3
4
If a Kermit transmitter waits more than KermitTimeoutSecs for an acknowledgement, it
resends the last packet. If a Kermit receiver waits more than KermitTimeoutSecs for the next
byte, it sends an error packet to the transmitter.
See also: TransmitTimeout
5
6
KermitWindowsTotal
read-only, run-time property
property KermitWindowsTotal : Word
! Returns the total number of Kermit sliding windows negotiated for the current transfer.
7
8
If sliding windows control is disabled, KermitWindowsTotal returns 0.
See also: KermitMaxWindows, KermitWindowsUsed
KermitWindowsUsed
9
10
11
property KermitWindowsUsed : Word
! Returns the number of Kermit sliding windows that currently contain data.
If sliding windows control is disabled, KermitWindowsUsed returns 0.
See also: KermitMaxWindows, KermitWindowsTotal
12
13
14
15
16
17
544 Chapter 14: File Transfer Protocols
1
1
read-only, run-time property
OnProtocolAccept
event
1
property OnProtocolAccept : TProtocolAcceptEvent
TProtocolAcceptEvent = procedure(CP : TObject;
var Accept : Boolean; var FName : TPassString) of object;
2
TPassString = string[255];
3
! Defines an event handler that is called as soon as the name of an incoming file is known.
This event handler provides an opportunity for the receiver to reject or rename the
incoming file. If an OnProtocolAccept handler is not installed, all files are accepted (subject
to the setting of the WriteFailAction property).
CP is the protocol component that is receiving the file. The event handler should set Accept
to True to accept the file, False to reject it. FName is the name of the file to be received. The
event handler can change the name if, for example, it would overwrite an existing file.
See “AcceptFile processing” on page 495 for more information.
4
6
7
See also: OnProtocolNextFile, WriteFailAction
OnProtocolError
event
property OnProtocolError : TProtocolErrorEvent
8
9
TProtocolErrorEvent = procedure(
CP : TObject; ErrorCode : SmallInt) of object;
! Defines an event handler that is called when an unrecoverable protocol error occurs.
10
This event is generated only for unrecoverable errors. Most protocol errors caused by line
noise are handled automatically by the protocol and are not reported to this event handler.
11
CP is the protocol component that generated the error. ErrorCode is a number indicating
the type of error.
12
Note that the OnProtocolFinish event is generated soon after the OnProtocolError event and
passes the same error code. OnProtocolFinish is generated for both successful and failed
transfers, so you may want to use it instead of an OnProtocolError handler.
13
See “Error handling” on page 488 for more information.
See also: BlockErrors, OnProtocolFinish
14
15
16
17
TApdProtocol Component 545
1
1
1
OnProtocolFinish
event
property OnProtocolFinish : TProtocolFinishEvent
2
TProtocolFinishEvent = procedure(
CP : TObject; ErrorCode : SmallInt) of object;
3
! Defines an event handler that is called when a protocol transfer ends.
4
5
6
7
8
9
This event is generated whether the protocol ends successfully or not. If it ends successfully,
ErrorCode is zero. Otherwise, ErrorCode is a number indicating the type of error. CP is the
protocol component that generated the error.
An application could use this handler to display a completion dialog box (needed only if a
protocol status event handler is not also in use) or to enable the scheduling of another file
transfer.
The following example displays a message whenever a protocol finishes, and enables an
associated terminal window to accept data again:
procedure TForm1.ApdProtocol1ProtocolFinish(
CP : TObject; ErrorCode : SmallInt);
begin
ShowMessage('Protocol finished: '+ErrorMsg(ErrorCode));
ApdTerminal1.Active := True;
end;
See also: InProgress, OnProtocolError
10
11
OnProtocolLog
event
property OnProtocolLog : TProtocolLogEvent
TProtocolLogEvent = procedure(CP : TObject; Log : Word) of object;
12
13
14
! Defines an event handler that is called at well-defined points during a protocol transfer.
The primary purpose of this event is to give applications a chance to log statistical
information about file transfers such as the transfer time and whether they succeeded or
failed. Applications can also use this event for start-up and cleanup activities such as
deleting partial files after unsuccessful downloads.
15
16
17
546 Chapter 14: File Transfer Protocols
1
1
CP is the protocol component that needs to be logged. Log is a code that indicates the
current state of the file transfer. The possible states are:
Log
State
lfReceiveStart
File receive is starting.
lfReceiveOK
File was received successfully.
lfReceiveFail
File receive failed.
lfReceiveSkip
File was skipped (rejected by receiver).
lfTransmitStart
File transmit is starting.
lfTransmitOK
File was transmitted successfully.
lfTransmitFail
File transmit failed.
lfTransmitSkip
File was skipped (rejected by receiver).
1
2
3
4
6
No other information is passed along with the event. Use protocol status properties such as
FileName and ElapsedTicks to get additional information about the state of the transfer.
7
See “Protocol logging” on page 493 for more information.
8
See also: ProtocolLog
OnProtocolNextFile
event
9
property OnProtocolNextFile : TProtocolNextFileEvent
TProtocolNextFileEvent = procedure(
CP : TObject; var FName : TPassString) of object;
10
11
TPassString = string[255];
! Defines an event handler that is called to determine the next file to transmit in a batch
12
transfer.
If no handler is installed for this event, Async Professional transmits the files that match the
DOS filemask assigned to the FileMask property. If you need to transmit a batch of files that
cannot be described by a single filemask, you need to install an event handler for
OnProtocolNextFile.
CP is the protocol component that is transmitting. The event handler should return the next
file to transmit in FName, or an empty string to terminate the batch.
13
14
15
See “NextFile processing” on page 494 for more information.
16
See also: FileMask
17
TApdProtocol Component 547
1
1
1
OnProtocolStatus
event
property OnProtocolStatus : TProtocolStatusEvent
2
3
4
5
6
7
8
9
TProtocolStatusEvent = procedure(
CP : TObject; Options : Word) of object;
! Defines an event handler that is called regularly during a file transfer.
This event is generated for each block transmitted or received, after the completion of each
major operation (e.g., renaming a file, detecting an error, ending the transfer), and at
intervals of StatusInterval ticks (by default 18 ticks, or about 1 second). The program can
use it to update a status display that informs the user about the protocol progress.
CP is the protocol component that is in progress. A number of the properties of this
component can be read to establish the status of the transfer. Options is set to apFirstCall (1)
on the first call to the handler, apLastCall (2) on the last call to the handler, and zero on all
other calls.
A predefined status component called TApdProtocolStatus is supplied with Async
Professional. For a standard protocol status window you can simply create an instance of
this component and assign it to the StatusDisplay property of the TApdProtocol component.
If you do so, there is no need to supply your own OnProtocolStatus event handler.
See “Protocol status” on page 489 for more information.
See also: StatusDisplay, StatusInterval
10
Overhead
11
property Overhead : Word
12
13
14
15
! Determines the number of overhead bytes per data block used by EstimateTransferSecs.
When a protocol transfers a data block, not all of the bytes are actually data from the file
being transferred. Some of them are part of the packet header and others may be used to
quote or escape data characters that cannot be transmitted as-is.
When you select a protocol by assigning to the ProtocolType property, the TApdProtocol
component assigns a default value to Overhead that matches the characteristics of the
protocol. For some protocols the exact value of Overhead depends on the actual data being
transmitted, but the default is a reasonable estimate that gives reasonable transfer time
estimates. If the estimates are consistently in error, you can assign a new value to Overhead.
See also: EstimateTransferSecs, TurnDelay
16
17
548 Chapter 14: File Transfer Protocols
1
1
property
ProtocolError
read-only, run-time property
1
property ProtocolError : Integer
! Returns the code of the last error returned by the protocol.
2
This property returns zero except for the first call after an error is encountered. See “Error
Handling and Exception Classes” on page 900 for a complete list of error codes.
3
See also: ProtocolStatus
ProtocolLog
property
4
property ProtocolLog : TApdProtocolLog
! An instance of a protocol logging component.
If ProtocolLog is nil, as it is by default, the protocol does not perform any automatic logging.
You can install an OnProtocolLog event handler to perform logging in this case.
If you create an instance of a TApdProtocolLog class (see page 583), or a descendant thereof,
and assign it to ProtocolLog, the protocol will log itself automatically.
ProtocolStatus
read-only, run-time property
6
7
8
9
property ProtocolStatus : Word
! Returns a code that indicates the current state of the protocol.
This property is most useful within an OnProtocolStatus event handler. See “Protocol
status” on page 489 for more information.
10
See also: ProtocolError
11
12
13
14
15
16
17
TApdProtocol Component 549
1
1
1
ProtocolType
property
property ProtocolType : TProtocolType
2
TProtocolType = (ptNoProtocol, ptXmodem, ptXmodemCRC, ptXmodem1K,
ptXmodem1KG, ptYmodem, ptYmodemG, ptZmodem, ptKermit, ptAscii);
3
Default: ptZmodem
! Determines the type of file transfer protocol.
4
5
6
7
8
9
Async Professional encapsulates all of the file transfer protocols that it supports into a single
component. To select a particular type of protocol, you must assign the desired type to the
ProtocolType property. You should generally assign to ProtocolType shortly after creating
the TApdProtocol component and before assigning other properties, since various defaults
are assigned whenever you change ProtocolType, and some properties are valid only when
ProtocolType has a particular value.
Assigning a new value to ProtocolType first deallocates any protocol-specific memory used
by the prior protocol, then allocates and initializes any structures required by the current
protocol.
You should generally not assign ptNoProtocol to ProtocolType, but it can be used to
deallocate previous protocol memory while temporarily not allocating new protocol
memory.
See also: BlockCheckMethod
10
RTSLowForWrite
11
property RTSLowForWrite : Boolean
property
Default: False
12
13
14
15
16
! Determines whether protocols force RTS low while writing received data to disk.
When RTSLowForWrite is set to True, hardware flow control is used to prevent the
transmitter from sending additional data while the receiver writes data to disk. As soon as
the disk write is finished, RTS is raised again. This feature might be required if other
Windows applications are being run at the same time as a protocol transfer or if the disk
driver leaves interrupts disabled for an excessive time.
In order for this option to be effective, disk write caching must be disabled.
If the protocol is transferring files using a modem, it might also be necessary to configure
the modem to react correctly to the RTS signal.
17
550 Chapter 14: File Transfer Protocols
1
1
StartReceive
method
1
procedure StartReceive;
! Tells the protocol to start receiving files.
2
The steps leading up to calling StartReceive look something like this:
3
1. Create a port component.
2. Create a protocol component.
4
3. Set ProtocolType.
4. Set other properties to customize the protocol.
5. Write suitable handlers for protocol events.
6
6. Call StartReceive.
StartReceive returns immediately and receives files in the background, occasionally
generating events to keep the application apprised of progress. When the protocol is
finished, either successfully or with a fatal error, it generates an OnProtocolFinish event and
its InProgress property starts returning False.
7
8
See also: ProtocolType, StartTransmit
StartTransmit
method
procedure StartTransmit;
9
10
! Tells the protocol to start transmitting files.
The steps leading up to calling StartTransmit look something like this:
1. Create a port component.
11
12
2. Create a protocol component.
3. Set ProtocolType.
13
4. Set other properties to customize the protocol.
5. Write suitable handlers for protocol events.
14
6. Set FileMask or use an OnProtocolNextFile event handler to return a list of files to
transmit.
15
7. Call StartTransmit.
16
17
TApdProtocol Component 551
1
1
1
2
StartTransmit returns immediately and transmits files in the background, occasionally
generating events to keep the application apprised of progress. When the protocol is
finished, either successfully or with a fatal error, it generates an OnProtocolFinish event and
its InProgress property starts returning False.
See also: FileMask, OnProtocolNextFile, ProtocolType, StartReceive
3
4
StatusDisplay
property
property StatusDisplay : TApdAbstractStatus
! An instance of a protocol status window.
5
If StatusDisplay is nil, as it is by default, the protocol does not provide an automatic status
window. You can install an OnProtocolStatus event handler to display status in this case.
6
If you create an instance of a class derived from TApdAbstractStatus, such as the provided
TApdProtocolStatus component (see page 582), and assign it to ProtocolStatus, the status
window will be displayed and updated automatically.
7
StatusInterval
8
9
property
property StatusInterval : Word
Default: 18
! The maximum number of clock ticks between OnProtocolStatus events.
10
11
The OnProtocolStatus event is generated for each block transmitted or received, after the
completion of each major operation (e.g., renaming a file, detecting an error, ending the
transfer), and at intervals of StatusInterval ticks.
This property also determines how frequently the StatusDisplay window is updated.
12
See also: OnProtocolStatus, StatusDisplay
13
14
15
16
17
552 Chapter 14: File Transfer Protocols
1
1
StatusMsg
method
1
function StatusMsg(const Status : Word) : string;
! Returns an English string for a protocol status code.
2
This routine is intended primarily for use in protocol status routines. It returns a status
string from the string table resource linked into your EXE. The string ID numbers
correspond to the values of the psXxx protocol status constants (see page 491). If the string
table doesn’t contain a string resource with the requested ID, an empty string is returned.
3
4
The returned string is never longer than MaxMessageLen (80) characters.
See also: ProtocolStatus
TotalErrors
read-only, run-time property
6
property TotalErrors : Word
! The number of errors encountered since the current file transfer was started.
7
This error count is reset whenever a new file is started. This property is most useful within
an OnProtocolStatus event handler. See “Protocol status” on page 489 for more
information.
8
See also: BlockErrors
9
TransmitTimeout
property
10
property TransmitTimeout : Word
Default: 1092
!
11
Determines the maximum time a sender will wait for the receiver to release flow control.
If the receiver blocks flow control for longer than TransmitTimeout ticks (60 seconds by
default), the protocol is aborted.
12
13
14
15
16
17
TApdProtocol Component 553
1
1
1
TurnDelay
property
property TurnDelay : Word
2
Default: 0
! Determines the turnaround delay, in milliseconds, per data block used by
3
4
5
6
7
8
EstimateTransferSecs.
When a protocol transfers a data block, the transmitter must often wait for an
acknowledgement from the receiver before it transmits the next block. This delay slows
down the overall throughput of the protocol and must be accounted for by
EstimateTransferSecs.
When you select a protocol by assigning to the ProtocolType property, the TApdProtocol
component assigns a default value to TurnDelay that is a good estimate for the given
protocol. However, the actual TurnDelay often depends on the characteristics of the
communications link between the sender and receiver (e.g., a satellite link would impose a
longer delay than a null modem cable). If the values returned by EstimateTransferSecs are
consistently in error, you can assign a new value to TurnDelay.
See also: EstimateTransferSecs, Overhead
UpcaseFileNames
property
9
property UpcaseFileNames : Boolean
10
Default: True
! Determines whether the protocol converts file names to upper case.
11
12
13
14
15
Applications provide file names to protocols in the OnProtocolNextFile event or by setting
the FileName property. File names can also be received as part of the protocol transfer.
Because the DOS/16-bit Windows file system stores all file names in upper case, the protocol
component converts file and path names to uppercase.
Windows 95/98 and Windows NT preserve the specified case in file names, although they
don’t normally use case to distinguish between file names. For example, the file name
“MixCase.Txt” is stored by the file system with the upper and lower case characters
preserved, however, it can be accessed by any combination of upper and lower case (e.g.,
“MIXCASE.TXT” or “mIXCAse.tXt”). If you want to display the preserved case in status
and log routines, set UpcaseFileNames to False.
16
17
554 Chapter 14: File Transfer Protocols
1
1
WriteFailAction
property
1
property WriteFailAction : TWriteFailAction
!
TWriteFailAction = (
wfWriteNone, wfWriteFail, wfWriteRename, wfWriteAnyway);
2
Default: wfWriteRename
3
Determines the receiver’s behavior when the destination file already exists.
4
You should assign one of the following values to WriteFailAction:
Value
Description
wfWriteFail
Fail the receive attempt.
wfWriteRename
Rename the incoming file.
wfWriteAnyway
Overwrite the existing file.
6
When wfWriteRename is selected and the destination file already exists, the first character in
the incoming file name is replaced with ‘$’ (e.g., “SAMPLE.DOC” becomes
“$AMPLE.DOC”). If that renamed file already exists, it is overwritten without warning.
The logic that handles these overwrite options is executed after the OnProtocolAccept event
has been generated. If you write an event handler that deals with possible overwrites, be sure
to set WriteFailAction to wfWriteAnyway before starting a transfer.
See also: OnProtocolAccept, ZmodemFileOption
XYmodemBlockWait
7
8
9
10
property
11
property XYmodemBlockWait : Word
Default: 91
! Determines the number of ticks Xmodem and Ymodem wait between blocks for a response
from the remote.
If the wait exceeds XYmodemBlockWait ticks, a sending protocol retransmits the block and
a receiving protocol aborts the transfer. The default wait is about 5 seconds.
See also: TransmitTimeout
12
13
14
15
16
17
TApdProtocol Component 555
1
1
1
Zmodem8K
property
property Zmodem8K : Boolean
2
Default: False
! Determines whether 8K blocks are enabled.
3
See “Large block support” on page 512 for more information.
4
ZmodemFileOption
property
property ZmodemFileOption : TZmodemFileOptions
5
6
TZmodemFileOptions = (zfoNoOption, zfoWriteNewerLonger,
zfoWriteCrc, zfoWriteAppend, zfoWriteClobber, zfoWriteNewer,
zfoWriteDifferent, zfoWriteProtect);
Default: zfoWriteNewer
7
! Determines the Zmodem file management options to use.
It should be assigned one of the following values:
8
9
10
11
12
13
14
15
Value
Description
zfoWriteNewerLonger
Transfer if new, newer or longer.
zfoWriteCrc
Not supported, treated same as WriteNewer.
zfoWriteAppend
Transfer if new, append if exists.
zfoWriteClobber
Transfer regardless.
zfoWriteNewer
Transfer if new or newer.
zfoWriteDifferent
Transfer if new or different dates or lengths.
zfoWriteProtect
Transfer only if new.
Regardless of the value of this property, new incoming files are accepted unless the
ZmodemSkipNoFile property is set to False.
The logic that handles these file management options is executed after the
OnProtocolAccept event has been generated. If you write an event handler that deals with
possible overwrites, be sure to set ZmodemFileOption to zfoWriteClobber before starting to
receive.
See also: ZmodemOptionOverride, ZmodemSkipNoFile
16
17
556 Chapter 14: File Transfer Protocols
1
1
ZmodemFinishRetry
property
1
property ZmodemFinishRetry : Word;
2
Default: 0
! Specifies the number of times to retry the final handshake of a Zmodem protocol session.
A Zmodem transmitter signals that it has no more files to transmit by sending a ZFin frame.
The receiver acknowledges this by sending its own ZFin frame. The transmitter then sends
“OO” as the final frame of the transfer.
The Zmodem specification indicates that this portion of the protocol isn’t critical (since all
files have already been completely received) and that a timeout while waiting for the
response should be ignored. However, this strategy doesn’t work well with DSZ, a Zmodem
implementation by Omen Technology, Inc.
DSZ retries after a ZFin timeout, which can sometimes cause unneeded packet transfers
when the handshake timeout is 10 seconds or less. To handle this situation, Async
Professional mimics DSZ when ZmodemFinishRetry is set a non-zero value. It waits
FinishWait ticks for a response.
ZmodemFinishRetry is the number of times to resend the ZFin in response to a timeout.
When ZmodemFinishRetry is zero the ZFin is sent only once. If no response is received the
protocol finishes without an error.
3
4
6
7
8
9
See also: FinishWait
ZmodemOptionOverride
property
property ZmodemOptionOverride : Boolean
10
11
Default: False
12
! Determines whether a remote sender’s options are ignored.
If ZmodemOptionOverride is set to True, a receiving protocol component ignores the
sender’s options and uses its own settings for ZmodemFileOption and ZmodemSkipNoFile.
Otherwise, it uses the sender’s options.
13
See also: ZmodemFileOption
14
15
16
17
TApdProtocol Component 557
1
1
1
ZmodemRecover
property
property ZmodemRecover : Boolean
2
Default: False
! Determines whether Zmodem performs file recovery.
3
4
5
6
7
8
9
Zmodem is capable of resuming interrupted file transfers if the receiver kept the partial file
when a previous transfer was interrupted. The transmitter requests this action by setting
ZmodemRecover to True. The request is transmitted to the receiver along with the file name
to be recovered. If the receiver has this file, it sends back the current file size. The transmitter
then adjusts its file offset and starts sending data from that point. If the receiver doesn’t
already have this file, a normal file transfer takes place.
See “Transfer resume” on page 510 for more information.
See also: InitialPosition
ZmodemSkipNoFile
property ZmodemSkipNoFile : Boolean
Default: False
! Determines whether a Zmodem receiver should skip all files that don’t already exist.
See also: ZmodemFileOption
10
11
12
13
14
15
16
17
558 Chapter 14: File Transfer Protocols
1
1
property
TApdFtpClient Component
1
The TApdFtpClient component is a specialized TApdWinsockPort that implements clientside file transfer protocol (FTP) capabilities as defined by RFC 959. TApdFtpClient presents
an intuitive interface that makes it easy to navigate and manipulate directories and files on
an FTP server.
2
Connecting and logging on to an FTP server is performed by the Login method. Logging off
and disconnecting is performed by Logout. Directory manipulation is performed by the
ChangeDir, Delete, ListDir, MakeDir, and Rename methods. File transfer and manipulation
is performed by the Delete, Rename, Retrieve, and Store methods. Server status and help
information are performed by the Help, and Status methods, and an arbitrary FTP
command string can be sent to the connected FTP server via SendFtpCommand.
Only one FTP operation is allowed at any given time, however these methods operate in an
asynchronous (i.e., non-blocking) fashion. This means that when a method is called to
initiate an FTP operation, it returns immediately and the operation is performed in the
background. When the operation is completed, the OnFtpStatus event is fired to notify the
application. During file transfer operations, the OnFtpStatus event is fired periodically to
provide status updates.
3
4
6
7
8
9
10
11
12
13
14
15
16
17
TApdFtpClient Component 559
1
1
1
Hierarchy
TComponent (VCL)
2
! TApdBaseComponent (OOMisc) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
" TApdComPort (AdPort) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
3
# TApdWinsockPort (AdWnPort) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
TApdCustomFtpClient (AdFtp)
4
TApdFtpClient (AdFtp)
5
Properties
6
7
Account
" InBuffFree
" StopBits
" BaseAddress
" InBuffUsed
" SWFlowOptions
8
" BufferFull
9
" ComHandle
13
14
15
16
" LineBreak
" TraceName
" LineError
" TraceSize
Connected
" LogHex
" Tracing
ConnectTimeout
" LogName
" CTS
" LogSize
" DataBits
" ModemStatus
" DCD
# Open
" DeltaDCD
" UseEventWord
UserLoggedIn
UserName
PassiveMode
! Version
Password
# WsAddress
" OutBuffFree
# WsLocalAddresses
" DeltaRI
" OutBuffUsed
# WsLocalAddressIndex
# DeviceLayer
" Output
# WsMode
" DSR
" OutSize
# WsPort
" DTR
" Parity
# WsSocksServerInfo
FileLength
FileType
" FlowState
FtpLog
560 Chapter 14: File Transfer Protocols
1
TransferTimeout
" DeltaDSR
17
1
" TapiMode
" TraceAllHex
" DeltaCTS
12
InProgress
" InSize
BytesTransferred
11
ServerAddress
" AutoOpen
" Baud
10
" HWFlowOptions
RestartAt
# WsTelnet
" RI
" XOffChar
" RS485Mode
" XOnChar
" RTS
Methods
1
" ForcePortOpen
" PutChar
" ActiveDeviceLayer
" GetBlock
" PutString
" AddDataTrigger
" GetChar
" RemoveAllTriggers
" AddStatusTrigger
Help
Abort
" AddTimerTrigger
" RemoveTrigger
" InitPort
Rename
" AddTraceEntry
ListDir
Retrieve
ChangeDir
Login
" CharReady
" CheckForString
Logout
MakeDir
" SendBreak
2
3
4
SendFtpCommand
" SetBreak
CurrentDir
" PeekBlock
" SetStatusTrigger
Delete
" PeekChar
" SetTimerTrigger
" FlushInBuffer
" ProcessCommunications
Status
" FlushOutBuffer
" PutBlock
Store
Events
6
7
8
OnFtpError
" OnTriggerAvail
" OnTriggerStatus
OnFtpLog
" OnTriggerData
" OnTriggerTimer
OnFtpReply
" OnTriggerLineError
# OnWsAccept
OnFtpStatus
" OnTriggerModemStatus
# OnWsConnect
" OnPortClose
" OnTriggerOutbuffFree
# OnWsDisconnect
" OnPortOpen
" OnTriggerOutbuffUsed
OnWsError
" OnTrigger
" OnTriggerOutSent
9
10
11
12
13
14
15
16
17
TApdFtpClient Component 561
1
1
1
Reference
Abort
2
3
method
procedure Abort;
! Terminates a file transfer in progress.
Calling Abort stops a file transfer that is in progress.
4
5
See also: Retrieve, Store
Account
property
property Account : string
6
! Specifies the user’s account information.
7
Account information is required by some FTP servers for login or storing files. If the server
requests account information to complete an operation, the Account string is automatically
sent to the server.
8
See also: Password, Login, UserName
BytesTransferred
read-only, run-time property
9
property BytesTransferred : Longint
10
11
12
! The number of bytes transferred so far in the current file.
When sending a file, BytesTransferred is the number of bytes written to the Winsock buffer,
which may be less than the number of bytes actually transmitted to the server. When
receiving a file, BytesTransferred is the actual number of bytes received. This is useful within
an OnFtpStatus event handler when the status code is scProgress.
See also: OnFtpStatus
13
14
15
16
17
562 Chapter 14: File Transfer Protocols
1
1
ChangeDir
method
1
function ChangeDir(const RemotePathName : string) : Boolean;
! Changes the current working directory on the FTP server.
2
RemotePathName specifies the directory at the server. If RemotePathName contains an
empty string, or a change working directory operation is not allowed given the current
protocol state, ChangeDir returns False, otherwise True is returned and the operation is
initiated.
If the directory is successfully changed at the server, the OnFtpStatus event is fired with the
scComplete status code. If the directory operation is rejected by the server, the OnFtpError
event is fired and the operation is terminated.
3
4
See also: OnFtpError, OnFtpStatus
6
Connected
read-only, run-time property
7
property Connected : Boolean
! Indicates whether a connection to an FTP server has been established.
While a control connection to an FTP server is open, Connected will return True. This does
not indicate that the user is currently logged in to the server. To determine if the user is
logged in, use the UserLoggedIn property.
8
9
See also: UserLoggedIn
ConnectTimeout
property
10
11
property ConnectTimeout : Integer
Default: 0
! ConnectTimeout determines the connection timeout when establishing the control
12
connection.
When establishing the initial control connection to the FTP server, the ConnectTimeout
property determines the timeout (in ticks) associated with the connection attempt. If
ConnectTimeout is 0 (the default), the TApdFTPClient will not timeout. If ConnectTimeout
> 0, the connection attempt will be terminated if a connection is not made within
ConnectTimeout ticks. If a timeout occurs, the OnFTPError event is generated with
ErrorCode = ecFtpConnectTimeout.
See also: Login
13
14
15
16
17
TApdFtpClient Component 563
1
1
1
CurrentDir
method
function CurrentDir : Boolean;
2
! Obtains the path name of the current working directory from the FTP server.
4
If a directory operation is not allowed given the current protocol state, CurrentDir returns
False, otherwise True is returned and the operation is initiated. When the server responds
with the requested information, the OnFtpStatus event is fired with the csCurrentDir
command status code, and the InfoText parameter will point to a null terminated string
containing the path name of the current working directory.
5
If the operation is rejected by the server, the OnFtpError event is fired and the operation is
terminated.
3
6
See also: OnFtpError, OnFtpStatus
Delete
7
8
9
method
function Delete(const RemotePathName : string) : Boolean;
! Removes a file or directory at an FTP server.
RemotePathName specifies the file or directory at the server. If RemotePathName is an
empty string or a delete operation is not allowed given the current protocol state, Delete
returns False, otherwise True is returned and the operation is initiated.
10
If the file or directory is successfully deleted at the server, the OnFtpStatus event is fired with
the scComplete status code. If the delete operation is rejected by the server, the OnFtpError
event is fired and the operation is terminated.
11
See also: OnFtpError, OnFtpStatus
12
FileLength
read-only, run-time property
property FileLength : Longint
13
14
15
! Returns the size of the file being transferred.
If the file size is not known, FileLength returns zero. This property is most useful within an
OnFtpStatus event handler when the status code is scProgress.
See also: BytesTransferred, OnFtpStatus
16
17
564 Chapter 14: File Transfer Protocols
1
1
FileType
property
1
property FileType : TFtpFileType
2
TFtpFileType = (ftAscii, ftBinary);
Default: ftAscii
3
! Specifies the file data type.
Transferring a file in the wrong format can damage the file so that it becomes unusable. This
is particularly true of binary files, which, if transferred using ASCII format, are no longer
usable. Be sure to set FileType prior to initiating a file transfer. For text files use ftAscii;
otherwise, use ftBinary.
FtpLog
4
property
6
property FtpLog : TApdFtpLog
! An instance of a FTP logging component.
7
If FtpLog is nil (the default), TApdFtpClient does not provide automatic logging. You can
install an OnFtpLog event handler to provide logging services in this case.
8
FtpLog is usually set automatically at design time to the first TApdFtpLog component that is
found on the form. If necessary, use the Object Inspector to select a different logging
component.
9
Setting the FtpLog property at run time is necessary only when using a dynamically created
logging component or when selecting among several logging components.
10
See also: OnFtpLog, TApdFtpLog
11
12
13
14
15
16
17
TApdFtpClient Component 565
1
1
1
Help
method
function Help(const Command : string) : Boolean;
2
3
4
! Obtains help information from an FTP server.
If Command is not an empty string, the FTP command syntax for the specified command is
obtained, otherwise the names of all the commands supported by the server are retrieved. If
the help operation is allowed given the current protocol state, Help returns False, otherwise
True is returned and the operation is initiated.
5
If the help operation is successful, the OnFtpStatus event is fired with the csDataAvail
command status code, and the InfoText parameter will point to a null terminated string
containing the raw text of the help information received from the server.
6
If the help operation is rejected by the server, the OnFtpError event is fired and the
operation is terminated.
7
See also: OnFtpError, OnFtpStatus
InProgress
8
9
10
11
property InProgress : Boolean
! Returns True if an FTP operation is currently in progress.
This property is important since a call to initiate a command to an FTP server returns
immediately to your code. If you do not use an OnFtpStatus event handler to detect when
the operation is complete, you can check InProgress in a polling loop.
See also: OnFtpStatus
ListDir
12
13
14
15
16
! Obtains a listing of contents of a remote directory.
RemotePathName specifies the remote directory at the server. If RemotePathName is an
empty string, the contents of the current working directory will be obtained. Set FullList to
True to request that full file information for each file in the directory be obtained from the
server, otherwise only file names will be obtained. If a list operation is not allowed given the
current protocol state, List returns False, otherwise True is returned and the operation is
initiated.
566 Chapter 14: File Transfer Protocols
1
method
function ListDir(
const RemotePathName : string; FullList : Boolean) : Boolean;
17
1
read-only, run-time property
If the list operation is successful, the OnFtpStatus event is fired with the csDataAvail
command status code, and the InfoText parameter will point to a null terminated string
containing the raw text of the directory listing received from the server.
1
If the list operation is rejected by the server, the OnFtpError event is fired and the operation
is terminated.
2
See also: OnFtpError, OnFtpStatus
3
Login
method
4
function Login : Boolean;
! Establishes an FTP session with the FTP server specified by ServerAddress.
The logon procedure consists of opening a port to establish a control connection to an FTP
server and logging on to the server with the user identification specified by UserName and
Password. The ServerAddress, UserName, and Password properties must be set prior to
calling Login. If login is allowed given the current protocol state the function returns True
immediately. Otherwise False is returned.
If a connection to the server is established, the OnFtpStatus event is fired with the scOpen
status code.
If the server authenticates the user identification, the OnFtpStatus event is fired with the
scLogin status code and the UserLoggedIn property is set to True. Otherwise, the
OnFtpError event is fired and the connection is left open. Subsequent calls to Login will
send the user identification information to the server via the existing connection. Call
Logout to close the existing connection.
6
7
8
9
10
If a connection to the server cannot be established, an EApdSocket exception is raised.
11
See also: ConnectTimeout, Logout, OnFtpError, OnFtpStatus, Password, ServerAddress,
UserLoggedIn, UserName
12
13
14
15
16
17
TApdFtpClient Component 567
1
1
1
Logout
method
function Logout : Boolean;
2
3
! Terminates the active FTP session and closes the control connection.
If a file transfer is in progress then the control connection will remain open until the transfer
has completed.
4
If logout is allowed given the current protocol state the function returns True immediately.
When the user is logged out by the server, the OnFtpStatus event is fired with the scLogout
status code and the UserLoggedIn property is set to False.
5
When the control connection port has closed, the OnFtpStatus event is fired with the
scClose status code.
6
See also: Login, OnFtpStatus, UserLoggedIn
MakeDir
method
7
function MakeDir(const RemotePathName : string) : Boolean;
8
9
10
11
12
13
14
! Creates the specified directory on the FTP server.
RemotePathName specifies the new directory at the server. If RemotePathName contains an
empty string, or a directory operation is not allowed given the current protocol state,
ChangeDir returns False, otherwise True is returned and the operation is initiated.
If the directory is successfully created at the server, the OnFtpStatus event is fired with the
scComplete status code. If the directory operation is rejected by the server, the OnFtpErrors
event is fired and the operation is terminated.
See also: OnFtpError, OnFtpStatus
OnFtpError
event
property OnFtpError : TFtpErrorEvent
TFtpErrorEvent = procedure(
Sender : TObject; ErrorCode : Integer;
ErrorText : PChar) of object;
! Defines an event handler that is called an FTP protocol error occurs.
15
16
The server has rejected the FTP operation attempted and the operation is terminated.
ErrorCode contains the FTP error code returned by the server, and ErrorText points to a
null terminated string containing the text of the error.
See also: FTP Error Codes
17
568 Chapter 14: File Transfer Protocols
1
1
OnFtpLog
event
1
property OnFtpLog : TFtpLogEvent
TFtpLogEvent = procedure(
Sender : TObject; LogCode : TFtpLogCode) of object;
2
TFtpLogCode = (lcClose, lcOpen, lcLogin,
lcLogout, lcDelete, lcRename, lcReceive, lcStore,
lcComplete, lcRestart, lcTimeout, lcUserAbort);
3
! Defines an event handler that is called at designated points during an FTP file operation.
4
The primary purpose of this event is to give the application a chance to log auditing
information about file operations during a FTP session.
See also: TApdFtpLog
6
OnFtpReply
event
property OnFtpReply : TFtpReplyEvent
7
TFtpReplyEvent = procedure(Sender : TObject;
ReplyCode : Integer; ReplyText : PChar) of object;
8
! Defines an event handler that is called when an FTP server returns a reply.
9
An FTP reply consists of a 3-digit alphanumeric code as defined in RFC 959, followed by
some text. ReplyCode contains the integer form of the 3-digit alphanumeric code, and
ReplyText points to a null terminated string containing the entire reply text.
10
The primary purpose of this event is to monitor the server’s response to the operations
initiated by the application. This event can be useful during debugging.
11
OnFtpStatus
event
12
property OnFtpStatus : TFtpStatusEvent
TFtpStatusEvent = procedure(Sender : TObject;
StatusCode : TFtpStatusCode; InfoText : PChar) of object;
13
TFtpStatusCode = (scClose, scOpen, scLogout, scLogin, scComplete,
scCurrentDir, scDataAvail, scProgress, scTransferOK, scTimeout);
14
! Defines an event handler that is called when the state of the FTP protocol changes.
StatusCode indicates the current state of the FTP client. When StatusCode equals
csDataAvail, InfoText points to a null terminated string containing raw text received from
the server. Otherwise InfoText is nil.
15
16
17
TApdFtpClient Component 569
1
1
1
The following describes the possible status codes:
Status
Meaning
2
scClose
The control connection port to the FTP server is closed.
The Login method must be called to re-connect to the FTP
server. All other functions are disabled.
3
scOpen
Connection to an FTP server has been established and the
control port is open. Login, Help, Status, and
SendFtpCommand functions are enabled. This status event
does not indicate that the server has authenticated the
login identification and so should not be used invoke
another FTP operation.
5
scLogin
The FTP server has authenticated the user login
identification. All functions except Login are enabled.
6
scLogout
The FTP server has logged the user out. If this status is
the result of a call to Logout, then it will be followed by
scClose status when the port closes and should not be used
in this case to log on as another user.
scComplete
This status event is fired upon the successful completion
of an FTP operation initiated by ChangeDir, Delete,
MakeDir, Rename, or SendFtpCommand.
scCurrentDir
Indicates that the current working directory information
initiated by CurrentDir is available. InfoText points to
the null-terminated text string containing the full path
name of the current working directory.
scDataAvail
Indicates that an information request initiated by Help,
ListDir, or Status has completed. InfoText points to
requested text.
scProgress
This status event is fired periodically during a file
transfer initiated by Retrieve or Store. The
BytesTransferred property contains the number of file data
bytes sent or received so far.
scTransferOk
A file transfer initiated by Retrieve or Store is complete.
scTimeout
The server has not responded during the interval defined by
TransferTimeout. The file transfer operation in progress
is terminated.
4
7
8
9
10
11
12
13
14
15
16
17
570 Chapter 14: File Transfer Protocols
1
1
OnWsError
event
1
property OnWsError : TWsErrorEvent
TWsErrorEvent = procedure (
Sender : TObject; ErrorCode : Integer) of object;
2
! Defines an event handler that is generated when a Winsock error occurs.
3
This event handler is generated when an unhandled Winsock error occurs within the
control or data connection. ErrorCode contains the error code returned by Winsock. See
“Error Handling and Exception Classes” on page 900 for a list of error codes.
Password
4
property
property Password : string
6
! Specifies the user’s login password.
FTP requires users to log in with a user name and password to gain access to that computer.
Set Password prior to calling Login when connecting to an FTP server.
7
Users who do not have a personal login account can gain access an FTP site with an
anonymous account. To log in with the anonymous account, set UserName to
ANONYMOUS and the password is your e-mail address.
8
9
See also: Account, Login, UserName
Rename
method
function Rename(
const RemotePathName, NewPathName : string) : Boolean;
10
11
! Renames a remote file or directory at an FTP server.
RemotePathName specifies the file or directory at the server. If RemotePathName or
NewPathName is an empty string or a rename operation is not allowed given the current
protocol state, Rename returns False, otherwise True is returned and the operation is
initiated.
If the file or directory is successfully renamed at the server, the OnFtpStatus event is fired
with the scComplete status code. If the rename operation is rejected by the server, the
OnFtpError event is fired and the operation is terminated.
12
13
14
15
See also: OnFtpError, OnFtpStatus
16
17
TApdFtpClient Component 571
1
1
1
RestartAt
run-time property
property RestartAt : Longint
2
3
4
5
6
7
8
9
10
! Specifies where to resume an interrupted file transfer.
If the FTP server supports resumable file transfer, it can be restarted at somewhere other
than the beginning of the file by calling Retrieve with rmRestart, or Store with smRestart.
The value contained by RestartAt is used to determine the byte location in the file to resume
the transfer.
When restarting a Retrieve operation, if RestartAt is zero, then the transfer will resume at
the end of the local file which is the point where the original transfer was interrupted.
Otherwise, the transfer will resume at the location specified by RestartAt and subsequent
data in the local file will be overwritten. If RestartAt is greater than the size of the local file,
then no transfer will take place and the call to Retrieve will return False.
When restarting a Store operation, the transfer will resume at the location specified by
RestartAt and subsequent data in the remote file will be overwritten. If RestartAt is zero the
entire file will be transferred. If RestartAt is greater than the size of the local file, then no
transfer will take place and the call to Store will return False.
See also: Retrieve, Store
Retrieve
method
function Retrieve(const RemotePathName, LocalPathName : string;
RetrieveMode : TFtpRetrieveMode) : Boolean;
TFtpRetrieveMode = (rmAppend, rmReplace, rmRestart);
11
12
13
14
15
16
! Retrieve transfers a file from the FTP server to the local machine.
RemotePathName specifies the file at the server, and LocalPathName specifies the pathname
of the file on the local machine. RetrieveMode specifies how data will be written to an
existing local file.
The file will be transferred according to the file type specified by the FileType property.
If the local file already exists: rmAppend specifies that the incoming file data will be
appended to the end of the file; rmReplace specifies that the contents of the local file will be
replaced; rmRestart specifies that either the contents of the local file will be replaced starting
at the location specified by the RestartAt property, or if RestartAt = 0, the incoming data will
be appended to the end of the file. See RestartAt for more information about restarting a file
transfer.
If the local file does not exist, it will be created.
17
572 Chapter 14: File Transfer Protocols
1
1
If either RemotePathName or LocalPathName contain an empty string, or a retrieve
operation is not allowed given the current protocol state, Retrieve returns False, otherwise
the file transfer is initiated, the InProgress property is set to True, and the function returns
True. Periodically during the transfer, the OnFtpStatus event is fired with the scProgress
status code. The BytesTransferred property contains the number of bytes written to
LocalFile so far.
If the file is successfully transferred, the InProgress property is set to False and the
OnFtpStatus event is fired with the scTransferOk status code.
If the transmission times out, then the OnFtpStatus event is fired with the scTimeout status
code. If the server rejects the transfer command, the OnFtpError event is fired and the
operation is terminated.
See also: BytesTransferred, FileType, InProgress, OnFtpError, OnFtpStatus, RestartAt,
TransferTimeout
SendFtpCommand
method
1
2
3
4
6
7
function SendFtpCommand(const FtpCmd : string) : Boolean;
! Sends a FTP protocol command to the server.
8
FtpCmd is an FTP command string as specified in RFC 959. The FTP commands that can
be issued via this method are restricted to those not requiring a data connection. Thus all
file transfer commands (e.g., STOR, RETR, etc.) and the LIST and NLST commands are
prohibited. To illustrate, here are a few accepted commands:
SendFtpCommand('CWD pub/apro');
SendFtpCommand('STAT pub');
SendFtpCommand('HELP RETR');
9
10
11
The function returns True immediately if the command is initiated, otherwise False is
returned. Upon successful completion, the OnFtpStatus event is fired with the scComplete
status code.
12
If the server rejects the command for some reason, then the OnFtpError event is fired and
the operation is terminated.
13
See also: OnFtpError, OnFtpStatus
14
15
16
17
TApdFtpClient Component 573
1
1
1
ServerAddress
property
property ServerAddress : string
2
3
4
5
! Specifies the FTP server’s IP address or host name.
ServerAddress accepts the IP address in dot notation (e.g., 165.212.210.10) or as a host
name (e.g., ftp.turbopower.com). If a host name is used, a DNS lookup is performed to
determine whether a DNS entry exists for the host name. If an IP address can be found, the
port is opened to establish an FTP control connection. If an IP address cannot be found, an
EApdSocketException is raised.
Status
method
function Status(const RemotePathName : string) : Boolean;
6
7
8
9
10
! Obtains status information from the FTP server.
RemotePathName specifies a file or directory at the server. If RemotePathName is an empty
string, general server status information is requested. If RemotePathName specifies a
directory at the server, a full listing of the directory contains is requested. If
RemotePathName specifies a file at the server, then the file size and timestamp are
requested.
If a status operation is not allowed given the current protocol state, Status returns False.
Otherwise, True is returned and the operation is initiated. When the server responds with
the requested status information, the OnFtpStatus event is fired with the csDataAvail
command status code, and the InfoText parameter will point to a null terminated string
containing the raw text of the status information.
11
If the operation is rejected by the server, the OnFtpError event is fired and the operation is
terminated.
12
See also: OnFtpError, OnFtpStatus
13
14
15
16
17
574 Chapter 14: File Transfer Protocols
1
1
Store
method
function Store(const RemotePathName, LocalPathName : string;
StoreMode : TFtpStoreMode) : Boolean;
TFtpStoreMode = (smAppend, smReplace, smUnique, smRestart);
! Transfers a file from the local machine to the FTP server.
1
2
3
RemotePathName specifies the file at the server. LocalPathName specifies the file on the
local machine. StoreMode identifies how the file will be written to an existing remote file.
4
The file will be transferred according to the file type specified by the FileType property. Be
sure to set FileType prior to initiating a file transfer. For text files use ftAscii, otherwise use
ftBinary.
If the remote file specified by RemotePathName already exists in the server’s working
directory, StoreMode controls the effect of the transfer according to the following values:
Value
Effect
smAppend
The local file data will be appended at the end of the remote
file.
smReplace
The contents of the remote file will be replaced.
smUnique
A remote file will be created with a unique name and the local
file will be written to it.
smRestart
Either the contents of the remote file will be replaced
starting at the location specified by the RestartAt property,
or, if RestartAt equals 0, the file data will be appended to
the end of the remote file. See RestartAt for more information
about restarting a file transfer.
6
7
8
9
10
11
If the remote file does not exist, it will be created in the server’s current working directory.
See ChangeDir for information about changing the server’s working directory.
12
13
14
15
16
17
TApdFtpClient Component 575
1
1
1
2
3
4
5
6
7
If either RemotePathName or LocalPathName contain an empty string, or a store operation
is not allowed given the current protocol state, Store returns False, otherwise the file transfer
is initiated, the InProgress property is set to True, and the Store returns True. Periodically
during the transfer, the OnFtpStatus event is fired with the scProgress status code. The
BytesTransferred property contains the number of bytes accepted so far by Winsock. When
the file has been successfully transferred, the InProgress property is set to False and the
OnFtpStatus event is fired with the scTransferOk status code.
If the transmission times out, then the OnFtpStatus event is fired with the scTimeout status
code. If the server rejects the transfer command, the OnFtpError event is fired and the
operation is terminated.
See also: BytesTransferred, FileType, InProgress, OnFtpError, OnFtpStatus, RestartAt,
TransferTimeout
TransferTimeout
property
property TransferTimeout : Integer
Default: 1092
8
9
10
11
! Determines the maximum time (ticks) to wait during file transfer.
During a file transfer operation, each time a block of data is written out to, or read in from
the FTP data connection, a timer is started with the time-out value specified by
TransferTimeout. If the timer times out before the next block of data is received or accepted
by Winsock, then the transfer operation is terminated, and the OnFtpStatus event is fired
with the scTimeout status code.
See also: OnFtpStatus, Retrieve, Store
UserLoggedIn
12
13
14
property UserLoggedIn : Boolean
! Indicates whether or not an FTP session is active.
This property can be checked periodically to determine if the user is logged in to an FTP
server and an FTP session currently underway.
See also: Login, Logout
15
16
17
576 Chapter 14: File Transfer Protocols
1
1
read-only, run-time property
UserName
property
1
property UserName : string
! Specifies the user’s login name.
2
FTP requires users to log in with a user name and password to gain access to the server. Be
sure to set Password prior to calling Login when connecting to an FTP server.
Users who do not have a personal login account can gain access an FTP site with an
anonymous account. To log in with the anonymous account, set UserName to
ANONYMOUS and the password is to the user’s e-mail address.
3
4
See also: Password, UserLogin
6
7
8
9
10
11
12
13
14
15
16
17
TApdFtpClient Component 577
1
1
1
TApdAbstractStatus Class
2
TApdAbstractStatus is an abstract class that defines the methods and properties needed by a
component that automatically displays status while a TApdProtocol component is in the
process of transferring a file. You generally won’t need to create a descendent class of your
own, since Async Professional supplies one, the TApdProtocolStatus component described
on page 582.
3
4
5
6
7
8
9
However, TApdProtocolStatus shows a particular set of information about a transfer in a
predefined format, and you may find that this format is not suitable for your needs. If that is
that case, you need to create your own descendant of TApdAbstractStatus. Probably the best
way to do so is to study the source code of TApdProtocolStatus (in the AdPStat unit) and its
associated form, TStandardDisplay.
The TApdAbstractStatus class contains an instance of a TForm that holds various controls
used to display the protocol status. You design this form, create an instance, and assign the
instance to the Display property of TApdAbstractStatus.
TApdAbstractStatus overrides the standard VCL properties Ctl3D, Position, and Visible and
the standard VCL method Show. When these routines are used in the status component, the
overridden versions perform the same actions on the associated Display form. Thus you can
display the status form by calling Show, erase it by setting Visible to False, adjust its position
by assigning to Position, and use 3D effects by setting Ctl3D to True.
11
Once you have created an instance of your TApdAbstractStatus descendant, you must assign
it to the StatusDisplay property of your TProtocol component. When the protocol needs to
update the status display it calls the UpdateDisplay method of TApdAbstractStatus, which
you must override in order to update your particular kind of status window.
12
The source code for the TApdProtocolStatus component (in the AdPStat unit) serves as a
comprehensive example of writing a TApdAbstractStatus descendant.
10
13
14
15
16
17
578 Chapter 14: File Transfer Protocols
1
1
Hierarchy
1
TComponent (VCL)
! TApdBaseComponent (OOMisc) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
TApdAbstractStatus (AdProtcl)
3
Properties
Display
Protocol
2
! Version
4
Methods
CreateDisplay
DestroyDisplay
UpdateDisplay
6
7
8
9
10
11
12
13
14
15
16
17
TApdAbstractStatus Class 579
1
1
1
Reference Section
CreateDisplay
virtual abstract method
2
procedure CreateDisplay; virtual; abstract;
3
4
5
6
! An abstract method that creates a form to display protocol status.
A descendant of TApdAbstractStatus must override this method with a routine that creates a
TForm component that contains various controls (typically of type TLabel) for displaying
the protocol status. The TForm should usually also contain a TButton control and
associated CancelClick event handler that allows the user to cancel the protocol.
CreateDisplay must then assign the instance of this form to the Display property.
See also: DestroyDisplay, Display
DestroyDisplay
7
8
9
procedure DestroyDisplay; virtual; abstract;
! An abstract method that destroys the display form.
A descendant of TApdAbstractStatus must override this method to destroy the TForm
instance created by CreateDisplay.
Display
10
11
12
14
15
16
! A reference to the form created by CreateDisplay.
CreateDisplay must assign a properly initialized instance of a TForm to this property.
UpdateDisplay can refer to this property to update the status window.
! The protocol component that is using the status component.
When deriving your own components from TApdAbstractStatus you will probably want to
reference TApdProtocol properties to display information about the progress of the
protocol. Use this property to do so. It is automatically initialized when you assign the status
component to the StatusDisplay property of TApdProtocol.
580 Chapter 14: File Transfer Protocols
1
property
property Protocol : TApdCustomProtocol
17
1
run-time property
property Display : TForm
Protocol
13
virtual abstract method
UpdateDisplay
method
1
procedure UpdateDisplay(First, Last : Boolean); virtual; abstract;
! An abstract method that writes the contents of the status window.
2
A descendant of TApdAbstractStatus must override this method to update the display form.
The TApdProtocol component calls this method regularly from its OnProtocolStatus event
handler.
On the very first call to UpdateDisplay, First equals True and UpdateDisplay should typically
call the Show method of Display to draw the outline and background of the status form. On
the very last call to UpdateDisplay, First equals False and UpdateDisplay should typically set
the Visible property of Display to False to erase the status window.
For all other calls to UpdateDisplay, First and Last both equal False. During these calls,
UpdateDisplay must update the various labels in the Display form. To get information about
the protocol status, it should use the Protocol field of TApdAbstractStatus to read the values
of various properties such as FileName and BytesTransferred. See “Protocol status” on
page 489 for a list of the most commonly used properties.
The CancelClick event handler, if one is provided, should call the CancelProtocol method of
TApdProtocol to terminate the protocol because the user clicked the Cancel button.
3
4
6
7
8
9
10
11
12
13
14
15
16
17
TApdAbstractStatus Class 581
1
1
1
TApdProtocolStatus Component
2
TApdProtocolStatus is a descendant of TApdAbstractStatus that implements a standard
protocol status display. All you need to do is create an instance of a TApdProtocolStatus
component and assign it to the StatusDisplay property of your TApdProtocol component.
TApdProtocolStatus includes all of the most-often used information about a protocol
transfer and it also provides a Cancel button so that the user can stop the protocol at any
time.
3
4
5
6
TApdProtocolStatus overrides all the abstract methods of TApdAbstractStatus.
TApdProtocolStatus has no methods that you must call or properties that you must adjust.
You might want to change the settings of the Ctl3D and Position properties to modify the
appearance of the window. Figure 14.6 shows the TStandardDisplay form that is associated
with a TApdProtocolStatus component.
7
8
9
10
11
12
Figure 14.6: The TApdProtocolStatus component’s TStandardDisplay form.
13
For an example of using a TApdProtocolStatus component, see the introduction to
TApdProtocol on page 523.
14
Hierarchy
15
TComponent (VCL)
TApdBaseComponent (OOMisc) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
16
17
TApdAbstractStatus (AdProtcl). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 578
TApdProtocolStatus (AdPStat)
582 Chapter 14: File Transfer Protocols
1
1
TApdProtocolLog Component
1
TApdProtocolLog is a small class that can be associated with a TApdProtocol component to
provide automatic protocol logging services. Just create an instance of TApdProtocolLog
and assign it to the ProtocolLog property of the TApdProtocol component.
2
TApdProtocolLog creates or appends to a text file whose name is given by the HistoryName
property. Each time the OnProtocolLog event of TApdProtocol is generated, the associated
TApdProtocolLog instance opens the file, writes a new line to it, and closes the file.
3
4
TApdProtocolLog also deletes the partial file that exists whenever a receive fails and the
protocol type is not Zmodem (which can resume interrupted transfers).
Following is a sample of the text file created by TApdProtocolLog:
Zmodem transmit started on 7/6/01 8:33:21 AM : C:\TEMP\PROJ1.EXE
Zmodem transmit finished OK 7/6/01 8:33:28 AM : C:\TEMP\PROJ1.EXE
Elapsed time:
0:07
CPS: 1792
Size: 12547
Zmodem transmit started on 7/6/01 8:33:28 AM : C:\TEMP\PROJ2.EXE
Zmodem transmit finished OK 7/6/01 8:33:37 AM : C:\TEMP\PROJ2.EXE
Elapsed time:
0:08
CPS: 1971
Size: 15775
Zmodem transmit started on 7/6/01 8:33:37 AM : C:\TEMP\PROJ2.EXE
Zmodem transmit failed C:\TEMP\PROJ2.EXE Cancel requested
Zmodem receive started on 7/6/01 8:34:03 AM : ZIPVO.PAS
Zmodem receive failed ZIPVO.PAS Cancel requested
8
9
10
12
TComponent (VCL)
! TApdBaseComponent (OOMisc) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
TApdProtocolLog (AdProtcl)
13
14
Properties
HistoryName
7
11
Hierarchy
DeleteFailed
6
Protocol
15
! Version
16
Methods
UpdateLog
17
TApdProtocolLog Component 583
1
1
1
Reference Section
DeleteFailed
property
2
property DeleteFailed : TDeleteFailed
3
TDeleteFailed = (dfNever, dfAlways, dfNonRecoverable);
Default: dfNonRecoverable
4
5
6
7
8
9
10
11
! Determines whether received files are deleted after a protocol failure.
When a protocol receive session fails, there might be a partially received file in the
destination directory (depending on when and why the session failed). DeleteFailed
controls whether a partial file is automatically deleted by the TApdProtocol OnProtocolLog
event handler. DeleteFailed can have one of the following values:
Value
Description
dfNever
Partial files are never deleted.
dfAlways
Partial files are always deleted.
dfNonRecoverable
Partial files are deleted if the protocol cannot
resume a failed transfer (all protocols except
Zmodem).
Regardless of the value of DeleteFailed, received files are never deleted when the protocol
error is ecCantWriteFile, since that error usually indicates that the receiver doesn’t want to
disturb an existing file with the same name.
HistoryName
property
property HistoryName : string
12
13
14
Default: “APRO.HIS”
! Determines the name of the file used to store the protocol log.
You should generally set the value of HistoryName before calling TApdProtocol’s
StartReceive or StartTransmit methods. However, because the log file is opened and closed
for each update, you can change HistoryName at any time you wish. If you set HistoryName
to an empty string, automatic logging is disabled until you assign a non-empty string.
15
16
17
584 Chapter 14: File Transfer Protocols
1
1
Protocol
property
1
property Protocol : TApdCustomProtocol
! The protocol component that is using the log component.
2
Protocol is automatically initialized when the ProtocolLog property of the owning protocol
component is set. You can change Protocol to assign the log component to a different
protocol component.
3
UpdateLog
virtual method
4
The Log parameter has the same values passed to the OnProtocolLog event handler of
TApdProtocol. UpdateLog creates or appends to the log file, builds and writes a text string
for each event, and closes the log file. Additionally, it deletes the partially received file if Log
equals lfReceiveFail and the protocol type is not Zmodem.
6
procedure UpdateLog(const Log : Word); virtual;
! Call for each protocol logging event.
7
Note that TApdProtocolLog contains a field named Protocol that UpdateLog uses to obtain
additional information about the protocol such as the FileName, FileLength, ElapsedTicks,
and ProtocolType.
8
See also: TApdProtocol.OnProtocolLog
9
10
11
12
13
14
15
16
17
TApdProtocolLog Component 585
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
586 Chapter 14: File Transfer Protocols
1
1
Chapter 15: Fax Components
1
2
Document transfer using facsimile (fax) machines has become quite common—you might
even say pervasive—in today’s business environment. Almost all of the currently
manufactured modems are faxmodems. A faxmodem is a standard data modem that also
has the ability, when used with appropriate software, to send and receive faxes. Since the
faxmodem is under program control, it can provide more sophisticated capabilities than a
dedicated fax machine. A few of the possibilities include database storage, editing, and
forwarding of received documents, as well as scheduled fax transmissions to multiple
recipients.
3
4
5
Unfortunately, controlling a faxmodem is a relatively complex task. As is typical for the
communications industry, faxmodem technology is governed by multiple, evolving,
incomplete standards. With the exception of the terse technical specifications offered by the
TIA/EIA committee that controls faxmodem standards, little has been written about
controlling faxmodems. The TIA/EIA specifications describe the bare necessities of
faxmodem behavior; many ambiguities must be resolved by research and experimentation.
6
7
8
Microsoft has provided various levels of fax services in successive versions of Windows and
is trying to broaden and homogenize its fax services over all of its Windows operating
systems and environments. Parts of the fax services are available to programmers, but the
documentation is both scarce and sparse, causing many programmers to look elsewhere for
fax services.
9
10
Async Professional overcomes this information shortage by providing a complete set of
routines for computer control of faxmodems. These routines cover all phases of faxmodem
usage including document conversion, fax printer drivers, and send/receive support for the
current generation of faxmodems. These routines have a structure similar to the file transfer
protocols, complete with the programming hooks that allow you to write full-featured
applications.
11
12
13
14
15
16
17
587
1
1
1
Faxmodem Control from an Application
2
Integrating faxmodem support into your application involves two central tasks:
• Document conversion
3
• Faxmodem send/receive
5
Document conversion means converting a file into a format suitable for fax transmission or
converting a received fax into a format suitable for further processing (viewing, printing,
etc.). Faxmodem send/receive covers all the steps needed to control a faxmodem when
sending and receiving fax documents.
6
Document conversion
7
Document conversion is the process of creating a compressed bitmap image suitable for fax
transmission. Async Professional can convert the following file formats:
4
• ASCII text files
8
9
10
11
12
13
14
15
16
• Windows bitmap image files (BMP)
• PC Paintbrush image files (PCX)
• Multi-page PCX files (DCX)
• Tagged Image File Format image files (TIFF)
Files that have been converted to fax format in this way, as well as files that have been
received by Async Professional’s fax routines, are given the extension APF (Async
Professional Fax).
Async Professional also includes a printer driver for Windows 95/98/ME, and Windows NT
4.0/2000 (see the README.TXT file for an up-to-date list of the supported environments)
that provides convenient conversion of virtually any document file by “printing” that file to
the fax printer driver. The fax printer driver converts the printed image to an APF file and
can optionally alert a fax transmit program that a file is now available for fax transmit.
Received faxes are stored in the compressed bitmap image (APF format), so the data must
be unpacked before you can view, print, edit, or otherwise process the fax. Async
Professional provides an unpacker component that can unpack fax file to image files, to
memory bitmaps, or for special processing by your application. A viewer component and a
printer component make it easy to view or print the fax.
17
588 Chapter 15: Fax Components
1
1
Faxmodem send/receive
1
Faxmodem send/receive is the process of sending the appropriate commands to a
faxmodem to prepare it for sending or receiving faxes, initiating or receiving a call,
transferring a bitmap image, and terminating the connection.
Like a file transfer protocol, a successful fax transmission requires cooperation between the
sender and receiver. A number of standards have been developed for this purpose. Early fax
machines communicated using what was known as the Group 1 and Group 2 protocols.
Although your fax machine may have a Group 1 button, which allows it to receive faxes (very
slowly) from an old Group 1 fax machine, all fax machines sold today support the Group 3
facsimile protocol. The faxmodems that Async Professional can control do not support
Group 1 or Group 2 at all. If you need to send or receive faxes with a Group 1 partner, keep
your old fax machine!
Within Group 3, there are currently two EIA/TIA standards for computer control of
faxmodems: Class I and Class II. To identify these classes, Async Professional uses Arabic
numerals (Class 1 and Class 2) rather than Roman numerals because the numbers are
clearer to read in the source code and documentation. Class 2 depends on somewhat more
sophisticated chips within the faxmodem than Class 1. Class 2 chips are capable of
negotiating certain fax transmission parameters without any feedback from the computer.
Many modem manufacturers began producing Class 2 faxmodems before the specification
was complete, using an interim version of the specification. When the specification was
finally ratified, it differed significantly from the interim Class 2 specification. In the
meantime, interim Class 2 modems had become a defacto standard and could not be
ignored by the specification committee. To distinguish between interim Class 2, which is
referred to as simply Class 2, the final Class 2 specification is formally identified as Class 2.0.
Over the past few years, the Class 1 standard has been improved upon to a slight degree also.
Recently, the EIA/TIA committee began the formalization process for the Class 1.0
standard. While this new standard introduces a few new optional features, it is primarily a
simple renaming of the old Class 1 standard.
2
3
4
6
7
8
9
10
11
12
13
14
15
16
17
Faxmodem Control from an Application 589
1
1
1
2
3
4
5
6
7
8
9
Faxmodem specifications
Wherever possible the faxmodem components insulate you from the details of document
conversion and faxmodem control. Just as with the file transfer protocols, you don’t need to
read and understand all of the faxmodem technical specifications to use the faxmodem
routines. However, it does help to have a basic understanding of the specifications. For
further information, see the technical specifications listed in Table 15.1.
Table 15.1: Technical specification documents
Document Number
Description
RS-465
Group 3 Facsimile Apparatus for Document Transmission.
RS-466
Procedures for Document Facsimile Transmission.
EIA/TIA-578
Asynchronous Facsimile DCE Control Standard.
EIA/TIA-592
Asynchronous Facsimile DCE Control Standard—Service
Class II.
These documents are available from the EIA and TIA organizations directly. They can also
be obtained from Global Engineering Documents, a company that distributes engineering
specifications of all kinds. You can reach them by telephone at 800-854-7179 or 303-7922181. Their fax number is 303-397-2740.
10
11
12
13
14
15
16
17
590 Chapter 15: Fax Components
1
1
Document Conversion
1
Faxmodems don’t transmit documents directly. Instead, they transmit a compressed bitmap
image in a format that is specific to Group 3 fax devices. The TApdFaxConverter component
provides methods for converting standard image file formats into this format. Async
Professional uses a proprietary image file format (APF) that stores bitmap data in this
Group 3 format. The TApdFaxUnpacker component provides methods for unpacking APF
files into standard image file formats.
2
3
4
Fax file format
The Group 3 compressed bitmap format was designed specifically for transmission of data
over possibly noisy lines. The compression technique results in a file that combines relatively
small size with ease of recovery from missing or garbled data. As the receiving fax machine
or faxmodem software detects bad compression strings, it simply discards them and starts
collecting valid strings again. No transmission time is spent calculating and sending block
check characters. A document received with line errors is usually missing just a few pixels; if
the received document looks bad enough, the sender must transmit again.
Although the format of each compressed raster line is defined by the Group 3 facsimile
specification, the APF file format used by Async Professional is proprietary. The data within
each raster line follows the Group 3 specification, but the file contains additional
information that makes it easier to manage and transmit the image.
6
7
8
9
An APF file is formatted as shown in Table 15.2.
10
Table 15.2: APF file format
11
fax header
12
header for page 1
data for page 1
13
.
.
14
.
header for page N
15
data for page N
You usually don’t need to understand this format in any detail, but the information is
documented here in case you need to write APF manipulation routines that aren’t provided
with Async Professional.
16
17
Document Conversion 591
1
1
1
2
3
4
5
6
7
8
9
10
11
12
The file always begins with a header that contains, among other information, the number of
pages in the fax. Each page of the document follows, including a page header and the
compressed page data. The fax header and page header structures are defined in detail later
in this section.
The page data is a series of compressed raster line images. The line image optionally begins
with a word containing the number of bytes in the compressed line. (This word is stored
only in APF files that are ready for transmission; it is used to aid in padding each line to
match timing parameters of the receiving fax machine.) The length word is followed by the
line image in Group 3 compression format. If a pixel is set, it corresponds to a black dot on
the original image; if it is clear, it corresponds to a white dot.
Fax images can be converted and stored using two different resolutions. Standard resolution
is 200 horizontal dots per inch by 100 vertical dots per inch. High resolution has the same
horizontal resolution, but uses 200 vertical dots per inch. In some fax documentation, the
resolutions are described as 98 dots per inch and 196 dots per inch. Those numbers are
actually more exact, but 100 and 200 are easier to remember and are commonly used in
most fax documentation.
The standard width of a fax page is 1728 pixels, or about 8.5 inches. Several optional widths
are also available. Async Professional supports only one of the optional widths: 2048 pixels
per row, or about 10 inches. There is no fundamental limit on fax page length. Even so, you’ll
probably want to limit it to 11 inches, or 14 inches if you want to mimic legal size paper. This
is especially important when you consider that many faxes are now printed on sheet-fed
laser printers.
You specify the fax resolution and horizontal width when a document is converted to an
APF file. The resolution and width of each page are stored in the page header. When the APF
file is later transmitted, the TApdSendFax component reads the page header to determine
the resolution and width to use to transmit the fax.
APF file header
Table 15.3 shows the fields in the APF file header structure, TFaxHeaderRec.
13
14
15
16
Table 15.3: APS file header fields
Field
Purpose
Signature
A unique string that identifies the file as an APF file.
FDateTime
Date and time that the file was created (in DOS format).
SenderID
The station ID of the fax device that transmitted the fax
data.
17
592 Chapter 15: Fax Components
1
1
Table 15.3: APS file header fields (continued)
1
Field
Purpose
Filler
A dummy byte used to align the rest of the header on a word
boundary.
PageCount
The number of pages in the APF file.
PageOfs
The offset, in bytes, of the first page in the file.
3
Padding
26 bytes of extra data, leaving room for future expansion
and forcing the size of the fax header to 64 bytes.
4
2
APF page header
Table 15.4 shows the fields in the APF page header structure, TPageHeaderRec.
6
Table 15.4: APF page header fields
Field
Purpose
ImgLength
The length of the compressed Group 3 data on the page.
ImgFlags
Flags describing the content of the page.
Padding
10 bytes of extra data, leaving room for future expansion
and forcing the size of the fax header to 16 bytes.
7
8
9
TGraphic registration of the APF format
The APF format has been registered as a TGraphic descendent. Components that make use
of TGraphic, such as TPicture, will be able to load and save the APF format.
The TGraphic descendent of the APF format can be used to convert other graphics formats,
like JPG, ICO, EMF and WMF to and from the APRO fax format. If you have additional
third party TGraphic descendents installed in your Delphi or C++ Builder environment,
these additional formats can be converted to and from the APRO APF format.
10
11
12
13
14
15
16
17
Document Conversion 593
1
1
1
TApdFaxConverter Component
2
Converting a document to APF format is the first step in the fax transmission process. You
can convert your documents just before you transmit them or you can convert them in
advance. If you like, this conversion process can be completely transparent to your users or
you can convert your documents in a separate step that is not immediately followed by
transmission. Async Professional also provides a Windows printer driver that can create
APF files.
3
4
6
The TApdFaxConverter component can be used to convert ASCII text, BMP, PCX, DCX,
and TIFF files to Async Professional’s proprietary file format. For input images not directly
supported by Async Professional, events and methods are published by the component that
allow the conversion of user-defined input images. Additionally, TApdFaxConverter can be
used for generically reading input image file for user-defined tasks.
7
ASCII text documents
5
8
9
10
11
12
13
14
15
The TApdFaxConverter component converts ASCII text files into APF files when the
InputDocumentType property of the converter is set to idText or idTextEx. Each text line of
the input file must end with a carriage return and a line feed. The converter can handle all
256 characters in the OEM character set. The converter cannot convert files that contain
embedded word processor formatting commands.
The text converter reads each line of the text file and converts the line into an appropriate
number of bitmapped raster lines. In essence, it converts each line into a picture of itself. To
do this, it uses a font table that contains a bitmap of each ASCII character. The number of
raster lines per text line depends on the pixel height of each character and the vertical
resolution of the fax conversion. The number of pixels in each raster line depends on the
pixel width of each character. For each raster line and each character in the line, the
converter finds the character bitmap in the font table and extracts the appropriate horizontal
pixel row. After looping through all the rows in the font, the converter has created a bitmap
image of the text line.
Async Professional can use any of the fonts available to Windows (such as the TrueType
fonts) when InputDocumentType is set to idTextEx, or it uses a set of built-in bitmapped
fonts when the InputDocumentType property is set to idText. The EnhFont property
controls which font is used when InputDocumentType is set to idTextEx.
16
17
594 Chapter 15: Fax Components
1
1
There are two built-in fonts available when InputDocumentType is set to idText – a standard
font (ffStandard) and a smaller font (ffSmall). ffSmall is a small 12x8 (12 pixels wide by 8
pixels high) font used for creating header lines at the top of each transmitted fax page.
ffStandard is a 20x16 font used for all other text. ffStandard was chosen to provide text lines
at 6 lines per inch (66 lines on a standard 11 inch page). If you specify a high resolution
image, the fonts are scaled vertically to 16 and 32 pixels, respectively.
The built-in fonts are stored in APFAX.FNT, which is 16KB. This font file can be distributed
with your applications. Alternatively, you can bind APFAX.FNT directly into your program
by activating the compiler define BindFaxFont in AWCVTFAX.PAS (it’s defined by default).
When BindFaxFont is defined, APFAX.RES (created from APFAX.FNT using Borland’s
Resource Workshop and a user-defined resource type) is linked into your program at
compile time. This adds about 16KB to your EXE file.
BMP, PCX, DCX, and TIFF graphic images
1
2
3
4
6
Async Professional provides document conversion routines for four popular graphics image
formats: BMP, PCX, DCX, and TIFF. The PCX format originated with the PC Paintbrush
program. The Async Professional conversion routines are tested with PCX images up
through version 3.0. DCX files are special container files that contain one or more PCX
images. TIFF (Tagged Image File Format) is a multi-platform format that is designed to
allow easy migration between platforms such as the Macintosh and the IBM PC. The Async
Professional conversion routines are tested with TIFF images up through version 4.0. BMP
files are standard Windows bitmap files.
Most Async Professional conversion routines work with monochrome images only. The
exception to this rule is the bitmap converter, which has the ability to dither color images.
Note that the dithering process is slower, so you should keep your images monochrome if
possible. Converting a BMP, PCX, or TIFF image file always produces an APF file containing
one fax page. Converting a DCX file produces a fax containing as many pages as are
contained in the DCX file.
Because BMP, PCX, DCX, and TIFF images are already a sequence of compressed raster
lines, the job of the TApdFaxConverter component is different than that done in the text
conversion process. For these files, the conversion routines unpack the images, then repack
them into the format required for faxing.
7
8
9
10
11
12
13
14
15
16
17
TApdFaxConverter Component 595
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
The images supported by Async Professional are stored assuming a pixel aspect ratio of 1 to
1, which means that the height of a pixel is the same as its width. For example, a 10 by 10 box
of pixels would appear on screen as a square, not a rectangle. Fax images, on the other hand,
have one of two aspect ratios. In high resolution, the aspect ratio is 1 to 1. In standard
resolution, the aspect ratio is 2 to 1. Hence, in a standard resolution fax, a 10 by 10 box of
pixels would not appear as a square. Instead it would be a rectangle twice as high as it is
wide.
Given the differences in aspect ratios, an image converted to a standard resolution APF
image appears distorted—tall and thin. To solve this problem, the TApdFaxConverter
component can either double the width of an image or halve the height of an image when it
is converted to a standard resolution fax and the modified image still fits on the page.
These behaviors are enabled by turning on the coDoubleWidth and coHalfHeight options,
respectively. The coDoubleWidth option is on by default. coDoubleWidth was chosen over
coHalfHeight, since doubling the width of the image does not discard data, whereas halving
the height of the image causes every other line to be discarded.
The TApdFaxConverter also includes another option, coCenterImage, that is used during
image file conversions. When this option is enabled, as it is by default, graphic images are
automatically centered horizontally on the fax page. See the Options property on page 619
for more information about these options.
Processing image files
There are times when you might find it useful to be able to process an image file (i.e., any file
that can be represented in the form of a bitmap) within your program. For instance, you
might want to implement a cover page editor that allows the user to open image files and put
them on the cover page, without having to convert the image into an APF file as an interim
step.
The TApdFaxConverter allows you to manually process image files, doing whatever you
wish with the raster data read from the file. This is done using the OpenFile, GetRasterLine,
and CloseFile methods. The first step in manually processing an image file is to set the
DocumentFile and InputDocumentType properties of the converter.
The next step is to call the component’s OpenFile method. This opens the image file, reads
any header data for the image, validates the header data, and returns control to you. One of
several exceptions can be raised depending on whether or not the input file exists and
whether or not the input file is a valid image file of the type specified by the
InputDocumentType property.
16
17
596 Chapter 15: Fax Components
1
1
After opening the image file, make one or more calls to the GetRasterLine method of the
converter. GetRasterLine takes four parameters: Buffer, BufLen, EndOfPage, and
MorePages.
1
Buffer is the buffer that receives the raster data you are reading. You should make this buffer
at least 512 bytes long. The actual length, in bytes, of the raster line is in BufLen upon return.
If the EndOfPage parameter is set to True on return, the end of the current page has been
reached. If MorePages is True, there are more pages in the file to be processed.
2
Lastly, you must call CloseFile to close the image file when you are done processing it.
Calling CloseFile closes the physical image file and disposes of several internal data
structures that are used to read the image file.
4
3
The following pseudo-code shows a typical use of the manual image processing methods of
the converter:
procedure ProcessImageFile(
FName : string; DocType : TFaxInputDocumentType);
var
Cvt : TApdFaxConverter;
Buffer : PByteArray; {type defined in SysUtils}
BufLen : Integer;
EndOfPage : Boolean;
MorePages : Boolean;
begin
GetMem(Buffer, 512);
6
7
8
9
10
try
Cvt := TApdFaxConverter.Create(nil);
except
FreeMem(Buffer, 512);
raise;
end;
11
12
Cvt.InputDocumentType := DocType;
Cvt.DocumentFile := FName;
13
try
Cvt.OpenFile;
except
Cvt.Free;
FreeMem(Buffer, 512);
raise;
end;
14
15
16
17
TApdFaxConverter Component 597
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
MorePages := True;
try
while MorePages do begin
...code for handling beginning of new page...
EndOfPage := False;
while not EndOfPage do begin
Cvt.GetRasterLine(Buffer^, BufLen, EndOfPage, MorePages);
...code to process data in Buffer...
end;
...code for handling end of page...
end;
finally
Cvt.CloseFile;
Cvt.Free;
FreeMem(Buffer, 512);
end;
end;
For a more complete demonstration of these features, see the EXIMAGE example program.
Converting user-defined image files
The TApdFaxConverter component allows you to convert image types that are not directly
supported. This is done through the OnOpenUserFile, OnCloseUserFile, and
OnReadUserLine events. When InputDocumentType equals idUser, the TApdFaxConverter
calls these events to open, close, and read the image file, instead of using its own internal
routines. These events allow you to create your own fax converter, if you know the format of
the image you need to convert.
OnOpenUserFile is called to open the user-defined image file. When this event is called, you
should open the physical image file, read its headers (if any), and allocate any buffers needed
for reading and processing the data.
OnReadUserLine is called to read a single line of raster data from the user-defined image
file. The two Boolean parameters passed to the event tell the converter when the end of an
input page is reached and whether there are any more pages to convert.
OnCloseUserFile is called to close the user-defined image file. When this event is called, you
should close the physical input file and deallocate any buffers that were allocated by the
OnOpenUserFile event.
The following example assumes a hypothetical image file type. This image file has a 4-byte
header. The first two bytes of the header are the width (in pixels) of the image, the next two
bytes contain the height (in pixels) of the image. The rest of the image file is raw,
17
598 Chapter 15: Fax Components
1
1
uncompressed, raster data. Each raster line in the file is just long enough to hold each line
(i.e., no padding). The OnOpenUserFile, OnReadUserLine, and OnCloseUserFile events
open, read, and close image files of this hypothetical image type.
1
type
TImageHeader = packed record
Width : Word;
Height : Word;
end;
2
var
Header : TImageHeader; InputFile : File; ReadLen : Integer;
BytesInFile : LongInt; BytesProcessed : LongInt;
procedure Form1.ApdFaxConverter1OpenUserFile(
F : TObject; FName : string);
begin
{open the physical file}
AssignFile(InputFile, FName);
Reset(InputFile, 1);
4
3
{read the header}
BlockRead(InputFile, Header, SizeOf(TImageHeader));
6
7
8
{calculate the length, in bytes, of each raster line}
ReadLen := (Header.Width + 7) shl 3;
{calculate the number of bytes in the file, for status info}
BytesInFile := FileSize(InputFile) - SizeOf(TImageHeader);
BytesProcessed := 0;
end;
procedure Form1.ApdFaxConverter1ReadUserLine(
F : TObject; Data : PByteArray; var Len : Integer;
var EndOfPage, MorePages : Boolean; var BytesRead,
BytesToRead : LongInt);
begin
{if we're at the end of the file, we're done}
EndOfPage := Eof(InputFile);
MorePages := False;
if EndOfPage then
Exit;
{read the next block of raster data}
BlockRead(InputFile, Data, ReadLen);
Len := ReadLen;
9
10
11
12
13
14
15
16
17
TApdFaxConverter Component 599
1
1
1
2
3
4
{update status information}
Inc(BytesProcessed, ReadLen);
BytesRead := BytesProcessed;
BytesToRead := BytesInFile;
end;
procedure Form1.ApdFaxConverter1CloseUserFile(F : TObject);
begin
{close image file}
CloseFile(InputFile);
end;
5
Example
6
This simple example demonstrates the steps involved in creating an APF file from an ASCII
text file. Create a new project, add the following components, and set the property values as
indicated in the following table:
7
Table 15.5: Example components and property values
Component
Property
Value
8
TApdFaxConverter
DocumentFile
C:\MYFILE.TXT (or some other
existing text file)
9
TApdFaxConverter
InputDocumentType
idText
TLabel
10
11
12
13
14
15
16
TButton
Double click on the TButton component. A shell for an OnClick event is generated for you.
Modify the generated code to match the following code:
procedure TForm1.Button1Click(Sender : TObject);
var
SaveCursor : TCursor;
begin
SaveCursor := Cursor;
Cursor := crHourglass;
try
ApdFaxConverter1.ConvertToFile;
finally
Cursor := SaveCursor;
end;
end;
17
600 Chapter 15: Fax Components
1
1
This event changes the form’s cursor to an hourglass, converts the file you specified in the
DocumentFile property, and then changes the cursor back to what it was before the
conversion.
Next, click on the TApdFaxConverter component, then click on the “Events” tab in the
Object Inspector. Double click on the “OnStatus” event. A shell of an OnStatus event
handler is generated for you. Modify the shell to match the following code:
procedure TForm1.ApdFaxConverter1Status(
F : TObject; Starting, Ending : Boolean;
PagesConverted, LinesConverted : Integer;
BytesToConvert, BytesConverted : LongInt;
var Abort : Boolean);
begin
if (BytesConverted <> 0) then begin
Label1.Caption := Format('Conversion is %d%% complete',
[(BytesToConvert * 100) div BytesConverted]);
Label1.Refresh;
end;
Abort := False;
end;
This procedure displays the progress of the conversion operation. You could also take
advantage of the “PagesConverted” and “LinesConverted” parameters to display additional
status information.
Now, save the project and run it. Click on the button. After a few moments, the hourglass
cursor should disappear and you will have, in the same directory as your input file, a file
called MYFILE.APF (where MYFILE is the first part of the filename that you chose for the
DocumentFile property). You can view this file with the VIEWER demonstration program,
or with the viewer in TCom.
1
2
3
4
6
7
8
9
10
11
The CVT2FAX demonstration program provides a more extensive example of converting
files to APF format.
12
Using shell execute
13
The TApdFaxConverter component can convert many file formats into APF files when the
InputDocumentType property is idShell. When the InputDocumentType property is
idShell, the TApdFaxConverter component will use the ShellExecute API method to execute
the application associated with the selected file type and print the document to the
TurboPower fax printer driver. Unlike other InputDocumentTypes that you may have used
in the past, idShell uses the application that created the document to print to the printer
driver (i.e. Microsoft Word would be the one to send a .DOC file). Essentially, this would
use the ShellExecute with the “printto” parameter to print the specified document to our
“APF Fax Printer” or “Print To Fax” depending on your operating system.
14
15
16
17
TApdFaxConverter Component 601
1
1
1
2
3
4
5
6
7
8
If the application associated with the selected file format does not support the “printto”
verb, but does support the “print” verb, then idShell will change the default printer to the fax
printer driver, print the document using ShellExecute and the “print” verb, and then change
back to the original default printer. For example, in Windows 95 Notepad does not support
the “printto”, but does support “print”, but both are supported in Windows 98 and 2000.
An exception is raised if the application does not support the “printto” or “print” verbs.
The following example converts C:\MYDOC.DOC to an APF file:
OpenDialog1.Filter := 'Any file(*.*)|*.*';
if OpenDialog1.Execute then begin
ApdFaxConverter1.DocumentFile := OpenDialog1.FileName;
ApdFaxConverter1.InputDocumentType := idShell;
ApdFaxConverter1.ConvertToFile;
end; //End if
The printer driver will not generate TApdFaxDriverInterface events when a document is
printed using the TApdFaxConverter component. When a document is being converted,
two registry keys are added. One is the window handle that will receive an APW_ENDDOC
message indicating that the print job is complete. The other is the name of the output file.
See the protected ConvertShell method (not documented) in AdFaxCnv.pas for details on
the specific registry keys. If either of these keys is present, the TApdFaxDriverInterface
component’s OnDocStart and OnDocEnd events are not generated.
9
10
11
12
13
14
15
16
17
602 Chapter 15: Fax Components
1
1
Hierarchy
1
TComponent (VCL)
! TApdBaseComponent (OOMisc) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
TApdCustomFaxConverter (AdFaxCvt)
2
3
TApdFaxConverter (AdFaxCvt)
4
Properties
DefUserExtension
LeftMargin
TabStop
DocumentFile
LinesPerPage
TopMargin
EnhFont
Options
FontFile
OutFileName
FontType
Resolution
InputDocumentType
StationID
! Version
Width
6
7
Methods
8
CloseFile
ConvertBitmapToFile
MakeEndOfPage
CompressRasterLine
ConvertToFile
OpenFile
Convert
GetRasterLine
9
10
Events
OnCloseUserFile
OnOutputLine
OnOpenUserFile
OnReadUserLine
OnStatus
11
12
13
14
15
16
17
TApdFaxConverter Component 603
1
1
1
Reference Section
CloseFile
method
2
procedure CloseFile;
3
! Closes an image file.
4
If you need to manually process input image files without converting them to APF files, use
CloseFile to close a previously opened (using OpenFile) image file. See “Processing image
files” on page 596 for more information.
5
See CompressRasterLine for an example of the use of CloseFile.
See also: CompressRasterLine, GetRasterLine, OpenFile
6
7
8
9
10
CompressRasterLine
method
procedure CompressRasterLine(
var Buffer, OutputData; var OutLen : Integer);
! Performs Group 3 compression on the data line.
If you need to write your own fax utilities and routines that output fax files, you must be able
to output Group 3 compressed raster data. Buffer should contain one raster line of data.
CompressRasterLine compresses the data in Buffer in Group 3 format and puts the result in
OutputData. OutputData should be at least 512 bytes long. OutLen is the length of the
compressed data.
For more information about APF files, see “Fax file format” on page 591.
11
12
13
14
15
The following example reads a line of raster data from an input file, compresses it, and writes
it to disk:
var
Buffer
OutBuf
BufLen
OutLen
EOP
More
OutFile
:
:
:
:
:
:
:
array[1..512] of Byte;
array[1..512] of Byte;
Integer;
Integer;
Boolean;
Boolean;
File;
...
16
17
604 Chapter 15: Fax Components
1
1
AssignFile(OutFile, 'C:\COMPRESS.IMG');
Rewrite(OutFile, 1);
...
ApdFaxConverter1.DocumentFile := 'C:\COMPRESS.IMG';
ApdFaxConverter1.OpenFile;
EOP := False;
while not EOP do begin
ApdFaxConverter1.GetRasterLine(Buffer, BufLen, EOP, More);
{make sure buffer length is 1728 pixels}
if (BufLen < 216) then
FillChar(Buffer[BufLen + 1], 0, 216 - BufLen);
ApdFaxConverter1.CompressRasterLine(Buffer, OutBuf, OutLen);
BlockWrite(OutFile, OutBuf, OutLen);
end;
CloseFile(OutFile);
ApdFaxConverter1.CloseFile;
1
2
3
4
6
See also: MakeEndOfPage
7
Convert
method
8
procedure Convert;
! Converts the input image file, outputting raster data to a user event.
Convert reads each raster line from the input image file (specified by the DocumentFile
property), compresses it in Group 3 format, and passes the compressed data to the
OnOutputLine event. In that event, process the compressed data (either output it to a file or
use it for some other purpose).
If InputDocumentType equals idUser, the OnOpenUserFile, OnReadUserLine, and
OnCloseUserFile events are called to open, read, and close the image file. See “Converting
user-defined image files” on page 598 for more information.
9
10
11
12
13
14
15
16
17
TApdFaxConverter Component 605
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
The following example converts an image file and writes the compressed Group 3 data to a
file:
const
OutFileOpened : Boolean = False; OutFile : File;
procedure TForm1.ApdFaxConverter1OutputLine(
F : TObject; Data : PByteArray; Len : Integer;
EndOfPage, MorePages : Boolean);
var
EOPBuf : array[1..64] of Byte;
EOPLen : Integer;
begin
if not OutFileOpened then begin
AssignFile(OutFile, 'C:\OUTPUT.IMG');
Rewrite(OutFile, 1);
OutFileOpened := True;
end;
if not EndOfPage then
BlockWrite(OutFile, Data^, Len);
if EndOfPage then begin
ApdFaxConverter1.MakeEndOfPage(EOPBuf, EOPLen);
BlockWrite(OutFile, EOPBuf, EOPLen);
if not MorePages then begin
CloseFile(OutFile);
OutFileOpened := False;
end;
end;
end;
...
ApdFaxConverter1.DocumentFile := OpenDialog.FileName;
ApdFaxConverter1.Convert;
See also: ConvertToFile, DocumentFile, InputDocumentType, OnCloseUserFile,
OnOpenUserFile, OnOutputLine, OnReadUserLine
14
15
16
17
606 Chapter 15: Fax Components
1
1
ConvertBitmapToFile
method
1
procedure ConvertBitmapToFile(Bmp : TBitmap);
! Converts a memory bitmap to an APF file.
2
ConvertToFile reads each raster line from the input bitmap (specified by the Bmp
parameter), compresses it in Group 3 format, and writes the compressed data (along with
the relevant document and page headers) in the APF file specified by OutFileName.
If the input bitmap is a color image, it will automatically get dithered to a monochrome
image as part of the conversion process. If the OutFileName property does not specify a file
extension, the default extension (DefFaxFileExt = “APF”) is appended to the output
filename.
See also: OutFileName
3
4
6
ConvertToFile
method
7
procedure ConvertToFile;
! Converts the input image file to an APF file.
ConvertToFile reads each raster line from the input image file (specified by DocumentFile),
compresses it in Group 3 format, and writes the compressed data (along with the relevant
document and page headers) in the APF file specified by OutFileName. If the
InputDocumentType is idBmp and the input bitmap is a color image, it will automatically
get dithered to a monochrome image as part of the conversion process.
If the OutFileName property does not specify a file extension, the default extension
(DefFaxFileExt = “APF”) is appended to the output filename. If InputDocumentType equals
idUser, the OnOpenUserFile, OnReadUserLine, and OnCloseUserFile events are called to
open, read, and close the image file. See “Converting user-defined image files” on page 598
for more information.
The following example converts a TIFF file to an APF file:
ApdFaxConverter1.DocumentFile := 'C:\MYIMAGE.TIF';
ApdFaxConverter1.InputDocumentType := idTiff;
ApdFaxConverter1.OutFileName := 'C:\FAX.APF';
ApdFaxConverter1.ConvertToFile;
See also: DocumentFile, InputDocumentType, OutFileName
8
9
10
11
12
13
14
15
16
17
TApdFaxConverter Component 607
1
1
1
DefUserExtension
property
property DefUserExtension : string
2
3
! The default extension for user-defined input image files.
When a user-defined image file is converted and the DocumentFile property does not
specify an extension, DefUserExtension, if any, is appended to the filename.
See “Converting user-defined image files” on page 598 for more information.
4
5
See also: OnCloseUserFile, OnOpenUserFile, OnReadUserLine
DocumentFile
property
property DocumentFile : string
6
! Specifies the name of the input image file.
7
When Convert, ConvertToFile, or OpenFile is called, the TApdFaxConverter component
attempts to open the file specified in DocumentFile.
8
If the filename specified in DocumentFile does not contain an extension, a default extension
is appended when the file is opened. The default extension depends on the value of
InputDocumentType:
9
10
11
12
13
InputDocumentType
Default Extension
idNone
No default
idText
TXT
idTextEx
TXT
idTiff
TIF
idPcx
PCX
idDcx
DCX
idBmp
BMP
idUser
The value of the DefUserExtension property
14
Unless you are sure of the current directory, the value of DocumentFile should be a fullyqualified (i.e., containing drive and directory information) filename.
15
The following example sets up a TApdFaxConverter to convert a TIFF file:
16
ApdFaxConverter1.DocumentFile := 'C:\MYIMAGE.TIF';
ApdFaxConverter1.InputDocumentType := idTiff;
See also: Convert, ConvertToFile, OpenFile
17
608 Chapter 15: Fax Components
1
1
EnhFont
property
1
property EnhFont : TFont
! Determines the font used by the fax converter.
2
If InputDocumentType is idTextEx, the FontFile and FontType properties are ignored and
the font specified by EnhFont is used by the fax converter instead. Any font available to
Windows can be used (double click on the property to invoke the font dialog and see a list of
the fonts). Only one font can be used for a document (i.e., font sizes and types cannot be
mixed within a single document).
3
4
There is an upper limit on the size of the font, but this limit is not typically reached unless a
very large font is used (e.g., greater than 72 pt). If the limit is exceeded, an ecEnhFontTooBig
error occurs during the conversion process.
The fax converter makes no attempt to keep all text on the page when the size of the font is
changed. You must ensure that the line length in the text file fits on the page in the desired
font. You might also need to adjust the LinesPerPage property to keep the lines on the page.
6
7
See also: FontFile, InputDocumentType
FontFile
property
property FontFile : string
8
9
! Specifies the filename of the font file used by the ASCII text converter.
When an ASCII text file is opened or converted and InputDocumentType is idText, built-in
fonts supplied in APFAX.FNT are used. If the compiler define BindFaxFont in
AWFAXCVT.PAS is activated (the default), APFAX.FNT is bound directly into your
program.
10
If BindFaxFont is not activated, the file specified in FontFile is loaded into memory. FontFile
must be the fully-qualified name of the font file.
12
See also: Convert, ConvertToFile, EnhFont, FontType, InputDocumentType, OpenFile
13
11
14
15
16
17
TApdFaxConverter Component 609
1
1
1
FontType
property
property FontType : TFaxFont
2
TFaxFont = (ffStandard, ffSmall);
Default: ffStandard
3
4
5
! Specifies the size of the font used to convert ASCII text files.
FontType is used only if InputDocumentType is idText (meaning that the default Async
Professional font file is used). The default font file contains two sizes of fonts. If you want to
use other fonts, see the EnhFont property.
6
ffStandard is a 20x16 font (20 pixels wide by 16 pixels high). This font allows about 8.5
characters per horizontal inch (about 85 characters per line in a standard width fax), and
about 12.5 lines per vertical inch.
7
ffSmall is a 12x8 font that allows for about 14 characters per horizontal inch (about 144
characters per line in a standard width fax), and about 25 lines per vertical inch.
The following example converts a text file to an APF file using a small font:
8
9
10
11
12
ApdFaxConverter1.DocumentFile := 'C:\MYFILE.TXT';
ApdFaxConverter1.InputDocumentType := idText;
ApdFaxConverter1.OutFileName := 'C:\FAX.APF';
ApdFaxConverter1.FontType := ffSmall;
ApdFaxConverter1.ConvertToFile;
See also: EnhFont, FontFile, InputDocumentType
GetRasterLine
method
procedure GetRasterLine(var Buffer; var BufLen : Integer;
var EndOfPage, MorePages : Boolean);
! Reads a raster line from an input image file.
13
GetRasterLine is used to manually read a line of raster data from an input image file. A call
to GetRasterLine must be preceded by a call to OpenFile.
14
GetRasterLine returns the raster data in Buffer. BufLen contains the length, in bytes, of the
raster data. EndOfPage is set to True if the end of the input page has been reached.
MorePages is set to True if there are additional pages in the input file.
15
See “Processing image files” on page 596 for more information about reading image files.
16
17
610 Chapter 15: Fax Components
1
1
The following example opens an image file, reads the data from it, and closes it:
var
Buffer
BufLen
EOP
More
:
:
:
:
1
array[1..512] of Byte;
Integer;
Boolean;
Boolean;
2
...
ApdFaxConverter1.DocumentFile := OpenDialog.FileName;
ApdFaxConverter1.OpenFile;
EOP := False;
while not EOP do begin
ApdFaxConverter1.GetRasterLine(Buffer, BufLen, EOP, More);
...process the image data...
end;
ApdFaxConverter1.CloseFile;
See also: OpenFile
3
4
6
7
InputDocumentType
property
property InputDocumentType : TFaxInputDocumentType
8
TFaxInputDocumentType = (idNone, idText, idTextEx, idTiff,
idPcx, idDcx, idBmp, idBitmap, idUser);
9
Default: idNone
10
! Specifies the type of the input image file.
The TApdFaxConverter component can read and convert a variety of input image files. With
the exception of idBmp and idBitmap, all input image files must be monochrome images.
That is, they can contain no more than one bit per pixel.
The value of InputDocumentType specifies the type of image. The following table shows the
possible values of InputDocumentType:
11
12
13
InputDocumentType
Image Type
idNone
No input image
idText
ASCII text file (using built-in fonts)
idTextEx
ASCII text file (using font specified by EnhFont)
idTiff
Tagged Image File Format (TIFF)
idPcx
PC Paintbrush image file (PCX)
idDcx
Multi-page PCX file
idBmp
Windows 3.x bitmap file
14
15
16
17
TApdFaxConverter Component 611
1
1
1
2
3
4
5
6
7
8
9
10
11
InputDocumentType
Image Type
idBitmap
Memory bitmap
idUser
User-defined input image
TApdFaxConverter, using the default font file, can convert CR/LF delimited ASCII text files
that have characters in the Windows OEM character set. If the text file contains non-OEM
characters, they will not be converted correctly unless you provide another font.
The idText and idTextEx values are both used for ASCII text files. If InputDocumentType is
idText, the Async Professional built-in fonts are used to convert the text file. This means that
the font in the file specified by FontFile is used. If InputDocumentType is idTextEx, one of
the Windows fonts is used to convert the text file. This means that the font specified by
EnhFont is used (FontFile and FontType are ignored).
TIFF files can be single or multi-strip images, but must contain either uncompressed raster
data or MacPaint compressed raster data. The byte-order of the file can be either Intel (little
endian, where words are stored in byte-reversed order) or Motorola (big endian, where
words are stored high byte first).
Input BMP files must be uncompressed.
User-defined input images can be in any format. You must ensure, however, that raster data
passed back to the TApdFaxConverter component is encoded such that one bit of raster data
represents one pixel of input image. See “Converting user-defined image files” on page 598
for more information about converting unsupported image files.
The following example sets a TApdFaxConverter component up to convert a text file:
ApdFaxConverter1.DocumentFile := 'C:\MYFILE.TXT';
ApdFaxConverter1.InputDocumentType := idText;
See also: Convert, ConvertToFile, EnhFont, FontFile, OpenFile
12
13
14
15
16
17
612 Chapter 15: Fax Components
1
1
LeftMargin
property
1
property LeftMargin : Cardinal
2
Default: 50
! Specifies the width in pixels of the left margin in the APF file.
To make output faxes look more attractive, the TApdFaxConverter can add a fixed left
margin to all pages in the fax. This is necessary for some fax machines (and some viewing
software) that print or display the left edge of the fax too close to the edge of the page or the
visible screen. The default left margin of 50 pixels provides a reasonable amount of white
space at the left edge of the page, without using too much of the horizontal space. On a
standard width fax, 50 pixels consumes only 3% of the horizontal space.
See also: TopMargin
LinesPerPage
3
4
6
property
7
property LinesPerPage : Cardinal
Default: 60
8
! The number of ASCII text lines on each fax page.
The TApdFaxConverter can convert a text file into a fax and leave all of the data on one page.
With large text files, however, this becomes a problem. Some continuous roll paper fax
machines can print a page like this on one sheet of paper, but it becomes extremely long if
the fax file is more than a few hundred lines.
It is probably more reasonable to break large text files up into multiple fax pages. To do this,
set the LinesPerPage property to the number of lines you want on each page. If you are using
the default font, the default LinesPerPage of 60 creates standard letter-sized pages about 10
inches long. If you are using one of the Windows fonts, you may have to experiment with
LinesPerPage to determine what fits on a page. If you want all of the text to appear on a
single fax page, set LinesPerPage to 0.
9
10
11
12
13
14
15
16
17
TApdFaxConverter Component 613
1
1
1
MakeEndOfPage
method
procedure MakeEndOfPage(var Buffer; var BufLen : Integer);
2
3
4
5
6
7
! Generates an end-of-page code.
Each fax page ends with a sequence of eight end-of-line fax codes. These codes indicate to
the receiving fax machine that the end of a page has been reached. If you are creating APF
files yourself, you must put these codes at the end of each page you create.
MakeEndOfPage puts eight end-of-line codes into Buffer. BufLen contains the length of the
codes (this length varies, depending on whether you are creating a standard width or wide
fax). You should write the data contained in Buffer to the end of the fax page you are
creating. The buffer passed to MakeEndOfPage should be at least 64 bytes in length.
For more information about APF files, see the “Fax file format” on page 591.
See the Convert method on page 605 for an example.
See also: Convert, Width
OnCloseUserFile
event
8
property OnCloseUserFile : TFaxCloseFileEvent
9
TFaxCloseFileEvent = procedure(F : TObject) of object;
! Defines an event handler that is called to close a user-defined image file.
10
11
12
13
When InputDocumentType is idUser, the TApdFaxConverter calls event handlers to open,
read, and close a user-defined image file. OnCloseUserFile is called to close the input file. F
contains a pointer to the fax converter component that called the event. When this event is
called, you should close the image file and destroy any buffers related to reading and
processing the image data.
See “Converting user-defined image files” on page 598 for more information.
See also: OnOpenUserFile, OnReadUserLine
14
15
16
17
614 Chapter 15: Fax Components
1
1
OnOpenUserFile
event
1
property OnOpenUserFile : TFaxOpenFileEvent
2
TFaxOpenFileEvent = procedure(
F : TObject; FName : string) of object;
! Defines an event handler that is called to open a user-defined image file.
When InputDocumentType is idUser, the TApdFaxConverter calls event handlers to open,
read, and close a user-defined image file. OnOpenUserFile is called to open the input file. F
contains a pointer to the fax converter component that called the event. FName contains the
name of the file.
3
4
Use the value passed in FName to open the file. Do not use the value in DocumentFile
because it is not guaranteed to have a file extension. FName is generated from the value in
DocumentFile and DefUserExtension (if there is not extension in DocumentFile).
6
See “Converting user-defined image files” on page 598 for an example of converting an
unsupported image file type.
7
The following example demonstrates the use of the OnOpenUserFile, OnReadUserLine, and
OnCloseUserFile events:
8
var
InputFile : File;
LineLen : Integer;
BytesProcessed : LongInt;
TotalBytes : LongInt;
9
10
procedure Form1.ApdFaxConverter1OpenUserFile(
F : TObject; FName : string);
begin
AssignFile(InputFile, FName);
Reset(InputFile, 1);
...read file header...
LineLen := WidthInBytesAsReadFromImageHeader;
BytesProcessed := 0;
TotalBytes := FileSize(InputFile) - SizeOf(Header);
end;
11
12
13
14
15
16
17
TApdFaxConverter Component 615
1
1
1
2
3
4
5
6
7
procedure Form1.ApdFaxConverter1ReadUserLine(
F : TObject; Data : PByteArray; var Len : Integer;
var EndOfPage, MorePages : Boolean; var BytesRead,
BytesToRead : LongInt);
begin
BlockRead(InputFile, Data^, LineLen, Len);
Inc(BytesProcessed, Len);
EndOfPage := Eof(InputFile);
MorePages := False;
BytesRead := BytesProcessed;
BytesToRead := TotalBytes;
end;
procedure Form1.ApdFaxConverter1CloseUserFile(F : TObject);
begin
CloseFile(InputFile);
end;
See also: OnCloseUserFile, OnReadUserLine
OnOutputLine
8
9
10
11
12
13
14
15
property OnOutputLine : TFaxOutputLineEvent
TFaxOutputLineEvent = procedure(
F : TObject; Data : PByteArray; Len : Integer;
EndOfPage, MorePages : Boolean) of object;
! Defines an event handler that is called to output a line of Group 3 compressed data.
When the Convert method is called, each line of raster data is read from the input image,
compressed in Group 3 format, and passed to the OnOutputLine event. You can then
process the compressed data.
F contains a pointer to the fax converter component that generated the event. Data is a
pointer to an array of bytes that contain the compressed data. Len is the length of the
compressed data.
If EndOfPage is True, the end of a page of input data has been reached. You should call
MakeEndOfPage at this point, to output an end-of-page code. If MorePages is True, there
are more pages of data to compress and output. If MorePages is False, you can dispose of any
buffers and other data that are required for your output.
See also: Convert, MakeEndOfPage
16
17
616 Chapter 15: Fax Components
1
1
event
OnReadUserLine
event
1
property OnReadUserLine : TFaxReadLineEvent
2
TFaxReadLineEvent = procedure(
F : TObject; Data : PByteArray; var Len : Integer;
var EndOfPage, MorePages : Boolean) of object;
! Defines an event handler that is called to read a line of data from a user-defined image file.
When InputDocumentType is idUser, the TApdFaxConverter calls event handlers to open,
read, and close a user-defined image file. OnReadUserLine is called each time a new line of
raster data must be compressed.Data is a pointer to a 0-based array of bytes. On return from
this function, it should contain a 1-bit-per-pixel representation of the data to be
compressed. The bits should be on for black pixels and off for white pixels. Len should be
equal to the length of the data.
EndOfPage should be set to True if the end of the user-defined page has been reached. If
EndOfPage is True, the value of MorePages should indicate whether any more pages are
available. If MorePages is True, the TApdFaxConverter begins a new page and begins calling
OnReadUserLine for more data. If MorePages is False, the conversion process ends. See
“Converting user-defined image files” on page 598 for more information.
3
4
6
7
8
See also: OnCloseUserFile, OnOpenUserFile, OnStatus
OnStatus
event
9
10
property OnStatus : TFaxStatusEvent
TFaxStatusEvent = procedure(
F : TObject; Starting, Ending : Boolean;
PagesConverted, LinesConverted : Integer;
BytesConverted, BytesToConvert : LongInt;
var Abort : Boolean) of object;
11
12
! Defines an event handler that is called to notify the user of the status of a conversion
operation.
13
During the conversion process, the TApdFaxConverter regularly calls the OnStatus event to
notify the user of the progress of the conversion.
14
If Starting is True, the conversion of the document is just beginning. This is the appropriate
time for you to do pre-conversion work (e.g., show the status display form).
15
If Ending is True, the conversion of the document is about to end. This is the appropriate
time for you to do post-conversion work (e.g., destroy the form that you were using to
display the conversion status).
16
17
TApdFaxConverter Component 617
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
PagesConverted is the number of pages that have been processed in the input document.
PagesConverted is equal to 1 after the conversion of the first page is started, then equal to 2
after the conversion of the second page is started, and so on. LinesConverted is the number
of raster lines that have been read and compressed on the current page.
BytesConverted is the number of image bytes that have been read from the input file.
BytesToConvert is the total number of bytes that will be read from the input file. These two
values can be used to create a “percent complete” style progress bar for the conversion
process.
Abort determines whether the conversion process will terminate prematurely. Set Abort to
True if you need to abort the conversion process.
The following example shows how to implement a percent complete indicator for a fax
converter:
procedure Form1.ApdFaxConverter1Status(
F : TObject; Starting, Ending : Boolean;
PagesConverted, LinesConverted : Integer;
BytesConverted, BytesToConvert : LongInt; var Abort : Boolean);
const
Frm : TConvertStatusForm = nil;
begin
if Starting then begin
Frm := TConvertStatusForm.Create(Application);
Frm.Show;
end else if Ending then begin
Frm.Close;
Frm.Free;
end else begin
if Frm.AbortBtnClicked then
Abort := True
else
{show progress}
Frm.Label1.Caption := Format(
'Conversion is %d percent complete',
[(BytesConverted * 100) div BytesToConvert]);
end;
end;
15
16
17
618 Chapter 15: Fax Components
1
1
OpenFile
method
1
procedure OpenFile;
! Opens an image file.
2
If you need to process an image file without converting it to an APF file, use OpenFile to
open the file specified by DocumentFile and InputDocumentType. Then use GetRasterLine
to read raster data from the image file. When you are finished reading the image, use
CloseFile to close the image file.
3
4
See “Processing image files” on page 596 for more information.
See also: CloseFile, CompressRasterLine, DocumentFile, GetRasterLine,
InputDocumentType
Options
property
property Options : TFaxCvtOptionsSet
6
7
TFaxCvtOptionsSet = Set of TFaxCvtOptions;
TFaxCvtOptions = (coDoubleWidth, coHalfHeight, coCenterImage,
coYield, coYieldOften);
Default: [coDoubleWidth, coCenterImage, coYield]
!