CLACom Frequently Asked Questions

Answering the Phone ====
You are using the SDI, as opposed to, the MDI Interface. You cannot use Threads in an SDI Application and the Phone Monitor needs to be started as a Thread.

The Phone Monitor needs to be able to open its own Window (with its own Accept Loop) so that it can operate in the background. In order for this to work in Clarion, your Application needs to use an "Application" Window as its Main Window. Threads in Clarion will only work if your Main Window is an Application Window.

If your program only needs to display a Dialog Box, with no Child Windows, then the Phone Monitor Template will not work, as written. What you can do, however, is copy the Source Code from the Phone Monitor and paste it into your Application. Most of the work is performed in the Timer Event.

Background File Transfers ====
Clarion is the only Application Generator that produces identical 16 and 32 Bit programs with a simple click of the mouse. 16 Bit Clarion programs have always been able to use features that other RAD environments can only implement in 32 bit programs.

Clarion uses the Accept Loop to accomplish much of its magic. In order for Background Threads to operate, they must have an Accept Loop. This does not mean, however, that a Window Procedure can enter into an Endless Loop and never check the Message Queue for Windows Messages. In Clarion, Timer Events are used to implement Background Processing.

The File Transfer routines are written in "C", not in the Clarion Language. They do not have a Clarion Window associated with them and hence do not have an Accept Loop. For this reason, the Transfer routines must take control over your Clarion Program while a File Transfer is in progress. If they didn't disable the Application Windows, your users would be able to select Menu Items, but the Clarion Program would not respond because an Accept Loop is not being Cycled.

Even if the Transfer Protocols were written in Clarion, they would have to use a Timer Event in order to operate in the Background and this would not be acceptable since the transfer would be horrendously slow.

It should be noted that the File Transfer routines do operate in the background, to a certain extent. This is evident by the fact that you can move a Window on top of your Application (while a transfer is taking place), move the Window away, and your Clarion Program will repaint its Window. The Transfer routines were designed to allow the Clarion Program to process its Messages.

In 32 Bit programs, CLACom Starts a Background Thread that does the actual File Transfer. Background threads are being utilized, they just may not be apparent.

Checksums and CRC's ====
CRC’s are normally 2 or 4 bytes, whereas a Checksum is a single byte that is computed by adding together the value of each character in a buffer. The high 8 bits are thrown away leaving an 8 byte checksum.

Instead of simply adding the values together, some implementations Exclusive OR the values, which offers slightly better error correction.

The following function can be used to compute a Checksum and with a slight modification, an XOR Checksum. This example uses a CSTRING as the source of data since it is assumed you will be working with data received from the Serial Port.

Prototype:

GetChecksum(*CSTRING),BYTE
Procedure:

 GetChecksum FUNCTION(RStr)

 Cnt         SHORT
 StrLen      SHORT
 Tally       SHORT
 ChkSum      BYTE

    CODE

    Tally  = 0

    StrLen = Len(Clip(RStr))

    ! Tally up the Checksum
    ! Comment Out or Remove the Line inside
    ! the Loop that you do not need.

    Loop Cnt = 1 to StrLen
       ! Compute a Regular Checksum

       Tally += Val(RStr[Cnt])

       ! Compute an Exclusive OR Checksum

       Tally = BXOR(Val(RStr[Cnt]),Tally)
    End

    ChkSum = Tally    ! Checksum = lower 8 bits

    Return(ChkSum)

CLACom’s 16 bit CRC function utilizes the CRC-CCITT calculation (also known as the “Xmodem CRC”). This is the function that is used to calculate a CRC for the Xmodem and Ymodem File transfers in CLACom. The Polynomial is:

    x16 + x12 + x5 + 1

If your application requires the CRC-16 calculation that uses a Polynomial of:

    x16 + x15 + x2 + 1

a file containing the Clarion code that calculates this CRC is available in the Private Download area for CLACom (the same place where you download the latest updates from).

If your application does use the CRC-CCITT Polynomial and GetCrc16() seems to be returning the wrong value, you may need to reverse the Bytes in the return value from GetCRC16(). There is no standard as to how the 2 bytes of the CRC are sent (or received). Some implementations send the Low order Byte first and some send the High order Byte first.

For the string 'TEXT', GetCRC16() returns 54512 or D4F0 hex. Reverse the Bytes and you have F0D4 hex or 61652.

Example Code to reverse the Bytes.

CRC16    USHORT
CRCVal   USHORT
CRCStr   CSTRING(256)
CRCStr = 'TEST'
CRC16 = GetCRC16(CRCStr, 4)
! Reverse the Bytes
CRCVal = BSHIFT(CRC16, 8) + BSHIFT(CRC16,-8)
If you need access to the individual Bytes then use a GROUP as shown below.

CRCGRP   GROUP
LoByte     BYTE
HiByte     BYTE
     END
CRC16    USHORT,OVER(CRCGRP)
CRCVal   USHORT
CRCStr   CSTRING(256)
CRCStr = 'TEST'
CRC16 = GetCRC16(CRCStr, 4)
! To reverse the bytes using the above GROUP
CRCVal = BSHIFT(CRCGRP.LoByte,8) + CRCGRP.HiByte

CLACom Version ====

The most reliable method is to right click on the CLACOM32.DLL file and select Properties. On the Version Tab (Detals Tab in Vista) you will find the Compile Date and Version Number.

The screen should look similar to the following (click for a larger view):

Alternatively, the File Modification Date on any of the DLL files should show a Date and Time that represents the Compile Date and the Version Number.

Clarion 5.x and 6 problems with Templates ====
With Clarion 5.5 and above, you need to select the CLACom Template procedure from the Defaults Tab on the Select Procedure Type window.

Compile Errors ====
We changed the Templates so that all of the Source Code is available in the Embeds. This way if you need to change the functionality of a Template Procedure, you no longer have to Edit the Template file. You can simply change the Embedded source code.

If you receive Compile Errors after Upgrading to a new version of CLACom, you should UnRegister the CLACom Template, ReRegister it, and if you still receive compile errors, perform the following steps:

If you are utilizing them, delete the PhoneMonitor, DialNumber and InitializePort Procedures. They will become "todo" Procedures.

Once removed, you can then add the Template Procedures back in to your Application.

There are two developer files, in addition to the CLACom32.DLL, that must remain synchronized. These are:

  • clacom.inc
  • clacom32.lib
  • clacom32.dll
If one or the other is out of sync, you will receive linker errors. Whenever you download a new version or restore from a backup, be sure that you reference the 3 files from the same release. For instance a version 6.0 clacom.inc will not work with a version 6.1 clacom32.lib.

Applications that utilize CLACom must include two files (Function Prototypes and Global Variable declarations) as well as one Import Library. The Example programs include these files, however the paths are relative to our Development Directory. You will need to fix the Path to the 2 Include Files and the Import Library.

In the Global Application Embeds, select "Inside the Global Map", and change the path to the CLACOM.INC file so that the file can be found during compile time. Do the same for the CLACOMST.INC file in the "Global Data" Embed.

You will also need to edit the Project, delete the CLACom Import Library (CLACOMxx.LIB) and then add it back in so that the Clarion Linker knows where to find it.

Dialing ====
For this task, you cannot use the DialNumber Template Procedure, since that routine expects to connect with a remote modem.

Please see the Phone Dialer Application, available in the Software Library, for an example program that demonstrates the techniques of working with Voice Modems.

Depending upon the capabilities of the Modem that is doing the Dialing, you have several options. Basically, you simply use ModemPuts() to send the Dial String. Make sure there are at least 2 tildes ('~') after the Dial String ('ATDT226-8033^M~~'). CLACom will send the Dial String, and then wait 1 second before returning. Upon returning, the Modem will have Dialed the Phone Number.

Now is where you run into problems and what you do from here on out is dependent upon the Modem's capabilities.

The very basic approach is to tell the Operator to pick up the Phone, wait a second, and then send a Carriage Return (ASCII 13) to the Modem to tell it to stop trying to Connect. But what do you do if the Operator doesn't pick up the Phone in 1 second? By sending the Carriage Return to the Modem, the Modem will release the line and stop trying to Connect. If the Operator hasn't picked up an Extension Phone, the Line will be dropped and the Operator will receive a Dial Tone. You could wait for 2 seconds before telling the Modem to drop the Line. This might work, and gives the Operator 2 seconds to pick up the Handset. But then again, what if it takes the Operator 3 seconds to pick up the Handset? 4 or 5 Seconds? If your program just blindly sends a Carriage Return to tell the Modem to stop trying to connect, you've lost control over the human intervention - the Operator using the program.

Generally, once you have sent a Dial String to the Modem, the Modem expects to wait 50 or 60 seconds for a Carrier from a Remote Modem. You can stop your Modem from waiting by sending any character (normally a Carriage Return) to your Modem. If a Handset is not picked up after the Modem has Dialed a Number, then the Modem will release the Line all by itself when it has either Timed Out or you send a Carriage Return to it to tell it to abort the Call.

The Modem is only being used as an interface to the Phone System in order to send the DTMF Dial Tones. Once the Modem has Dialed a Number, the Modem can be "released". However, before releasing the modem, Somebody has to engage the Line by picking up an Extension Phone to keep the line Open.

The best approach is to use Modems that return Call Progress status (i.e., BUSY, VOICE, etc). When implementing an Automatic Dialer to do Sales Calls or any task where you are trying to Dial a Human, as opposed to another Modem, what you should be most concerned with is the "VOICE" response from the Modem. Not all Modems support a "VOICE" return code. USRobotics Modems do, some Rockwell Chip Set Modems do, but most do not.

When using a Modem to Dial a Voice Number in order to get a Human on the other end, you need to look for the word "VOICE" from your Modem. If you receive BUSY, CONNECT, or you time out, there is no sense telling the Operator of your Program to pick up the Phone and start talking.

Assuming you have a Modem (or block of Modems) that return the Result Code of "VOICE" if a Human answers the Phone, then after sending the Dial String to Dial a Number, you need to Monitor the incoming Serial Port Data Stream for the word "VOICE". You would "time out" after 60 seconds if nothing comes in, and abort the call if CONNECT comes in (which means you connected to another Modem), or you receive a "BUSY" string.

If your Modem does not return the "VOICE" Result Code, then simply Dial the Number, tell the Operator to "Pick Up the Handset", and send a Carriage Return to the Modem to tell it to Disconnect. Many programs provide an OK Button for the Operator to click on, to tell the program that the Handset has been picked up (the program tells the Modem to drop the Line only after the button has been pushed).

Also, have you noticed that extra Phone Jack on the back of the Modem? There should be 2 jacks. One labeled "Line" and one labeled "Phone". If you plug a Phone into the "Phone Jack", and pick up the attached Phone after the Modem has dialed a Number, the Modem should automatically release itself from the Phone Line. This is what this jack is provided for.

DialNumber ====
Your program was, no doubt, compiled as a 16 bit EXE.

Windows NT barely tolerates 16 bit programs. If at all possible, they should not be used under NT.

The problem you are experiencing is due to the fact that the 16 bit NT Comm Driver does not support obtaining Modem Status. The 16 bit Comm API does not provide a means to obtain Modem Status. Microsoft provides an undocumented workaround which works fine in Win 3.x and Win 95/98. Apparently, however, Microsoft decided that Windows NT would not support this undocumented method of obtaining Modem Status.

PhoneMonitor and DialNumber both make a call to IsCd to determine if a Carrier with a remote modem has been established. IsCd, in turn, calls ModemStat to determine the current state of the Modem Status Register. The Modem Status Register provides a Bit Map that allows you to determine if there is a Carrier Signal. In a 16 bit program running under NT, ModemStat returns 0 which causes IsCd to return 0, which causes the Phone Monitor and DialNumber to fail to realize that a Connection has been established.

The only solution is to use the 32 bit Comm Driver which requires that your program be compiled as a 32 bit program using the 32 bit CLACom DLL.

Double Characters with Terminal in C5 ====
C5 seems to be Peeking at its Messages as well as Retrieving them from the Message Queue.

This Peek and the Retrieve was causing problems for the Terminal Emulator because it needs to take over the Clarion Message Loop in order to determine if a key was pressed (Clarion filters the WM_CHAR messages that Windows sends to a program). This is the reason for the Double Characters - the Hook Function that the Terminal uses was seeing the WM_CHAR message twice - once for the Peek and once for the actual Retrieval by the Clarion Message Loop.

CLACom works around this problem and the Terminal Emulator works just fine with C5, but if you've been working with a version of Clarion prior to version 5, you will need to reimport the CLATERM.TXA file into your APP. This applies, even if you've downloaded a new version of CLACom and are having the double character problem.

See the Help File to determine which TXA file you need to Import. Starting with version 4 of Clarion, there are 2 TXA files for Version 4 and Version 5 - one for the ABC Templates and one for the Standard Templates (4 separate files that you can choose from - in addition to the Version 2 TXA file!).

If you've modified the Terminal Emulator, the Quick Fix is as follows:

In the TermHookProc Terminal Procedure, replace this:

    ! Send a User Defined message that
    ! will slip thru
    PostMessage(TermClientWin,EVENT:WM_CHAR,
                WMsg:wParam,WMsg:lParam)
with this:

    ! Send a User Defined message that
    ! will slip thru
    If wParam = 1
       PostMessage(TermClientWin,EVENT:WM_CHAR,
                   WMsg:wParam,WMsg:lParam)
    End

Example Programs ====
Applications that utilize CLACom must include two files (Function Prototypes and Global Variable declarations) as well as one Import Library. The Example programs include these files, however the paths are relative to our Development Directory. You will need to fix the Path to the 2 Include Files and the Import Library.

In the Global Application Embeds, select "Inside the Global Map", and change the path to the CLACOM.INC file so that the file can be found during compile time. Do the same for the CLACOMST.INC file in the "Global Data" Embed.

You will also need to edit the Project, delete the CLACom Import Library (CLACOM

Hand Coders ====
CLACom is written in "C" and interfaces to your Clarion Application as a DLL. You are not required to use any of the Templates. They are simply provided to make certain Communications Tasks a bit easier (such as Dialing a Phone Number, Answering the Phone, etc).

If you would like to use the prewritten Template code, you can easily copy the code from the Template file and create your own Hand Written Procedures.

Hiding Windows ====
Create the Window with a Dimension of 1 by 1. Turn off all attributes so that Clarion doesn't have a hard time dealing with the Window. After Opening the Window, or in the Open Window Event, use Target{PROP:Hide}=1 to Hide the Window.

IsCd ====
Your program was, no doubt, compiled as a 16 bit EXE.

Windows NT barely tolerates 16 bit programs. If at all possible, they should not be used under NT.

The problem you are experiencing is due to the fact that the 16 bit NT Comm Driver does not support obtaining Modem Status. The 16 bit Comm API does not provide a means to obtain Modem Status. Microsoft provides an undocumented workaround which works fine in Win 3.x and Win 95/98. Apparently, however, Microsoft decided that Windows NT would not support this undocumented method of obtaining Modem Status.

PhoneMonitor and DialNumber both make a call to IsCd to determine if a Carrier with a remote modem has been established. IsCd, in turn, calls ModemStat to determine the current state of the Modem Status Register. The Modem Status Register provides a Bit Map that allows you to determine if there is a Carrier Signal. In a 16 bit program running under NT, ModemStat returns 0 which causes IsCd to return 0, which causes the Phone Monitor and DialNumber to fail to realize that a Connection has been established.

The only solution is to use the 32 bit Comm Driver which requires that your program be compiled as a 32 bit program using the 32 bit CLACom DLL.

Lock Ups ====
You no doubt have Hardware Flow Control (CTS/RTS) turned On (this is the default when calling SetPort).

The "Lock-Up" you are experiencing is the Windows Communications Driver waiting for the Clear To Send (CTS) Line to become active. This is occurring at the Device Driver level, not in the CLACom Library.

If the Device doesn't use Hardware Handshaking (CTS/RTS) you can turn that off by using either SetPortEx() or making a call to SetFlowControl() after calling SetPort(). You can also check the Modem Status line after you call SetPort() and InitPort() to determine if the device is attached and/or powered on.

Example:

! This has to be done after calling
! InitPort().
! This assumes that the device supports
! the CTS Line. If it doesn't, then you
! shouldn't be using CTS/RTS Flow Control.
modstat BYTE
modstat = ModemStat(ComPort)
If BAND(modstat, 010h) NOT= 010h
! Device is not hooked up or is not
! powered on. Display a message and
! tell user about situation. Don't
! send any data as Windows will hang
End
It is up to the Application/Programmer to determine if a device is attached and/or turned on. CLACom cannot do this for you since it only talks to Serial Ports, not devices that are attached to those ports.

The ModemInstalled Template Procedure is provided so that you may check to see if a Modem (or other Device that uses CTS Handshaking) is attached and powered on. The GTERM sample program shows how to utilize this procedure.

Long File Names ====
Clarion needs to maintain compatibility with 16 bit programs. Because of this compatibility issue, 32 bit programs are "stuck" with a 16 bit interface unless you directly call the 32 bit WIN API functions.

CLACom provides Templates and Functions that you can use in your Application to utilize Long File Names and the Explorer type File Open/Save Dialog Boxes. There is a Template procedure called GetOpenSaveFile() that will allow you to prompt a user for a file to Open or Save As. There is also a Control that you can use on your Windows that provides the standard "ellipses" button. If a user clicks on the button, the Enhanced Explorer type File Open Dialog Box appears instead of the dreaded 16 bit Dialog Box.

Note that starting with Version 4 of Clarion, the FileDialog() Procedure allows for Long filenames and Enhanced Explorer type Dialog Boxes. If you haven't yet upgraded to Clarion 4 or 5, you might want to consider doing so.

ModemStat ====
Your program was, no doubt, compiled as a 16 bit EXE.

Windows NT barely tolerates 16 bit programs. If at all possible, they should not be used under NT.

The problem you are experiencing is due to the fact that the 16 bit NT Comm Driver does not support obtaining Modem Status. The 16 bit Comm API does not provide a means to obtain Modem Status. Microsoft provides an undocumented workaround which works fine in Win 3.x and Win 95/98. Apparently, however, Microsoft decided that Windows NT would not support this undocumented method of obtaining Modem Status.

PhoneMonitor and DialNumber both make a call to IsCd to determine if a Carrier with a remote modem has been established. IsCd, in turn, calls ModemStat to determine the current state of the Modem Status Register. The Modem Status Register provides a Bit Map that allows you to determine if there is a Carrier Signal. In a 16 bit program running under NT, ModemStat returns 0 which causes IsCd to return 0, which causes the Phone Monitor and DialNumber to fail to realize that a Connection has been established.

The only solution is to use the 32 bit Comm Driver which requires that your program be compiled as a 32 bit program using the 32 bit CLACom DLL.

Multitasking ====
To simultaneously monitor and process data on 2 or more Serial Ports, you need to use the Timer Event and process data in "chunks". Both of your "started" Procedures need to have a Window associated with them. This is because you need to do your work inside of an Accept Loop. The Windows, themselves, can be hidden if you have no need to display data.

In order for other Threads to run, you must CYCLE the Accept Loop. You do this by "falling through" to the End of the Accept Loop or by issuing an explicit Cycle command. If you need to import 100 or 1000 records that were received over the Serial Port, into a Database, no other Thread will run until you are finished importing the records. In 16 Bit Windows, no other Windows Program will run until you are finished.

The following is an Example of how things were done in DOS programs:

Set(MyDataBase)
Loop
   Next(MyDataBase)
   If ErrorCode()
     Break
   End
End
If there are 1000 records to process, the Loop would not terminate until an Error occurs or EOF is reached. If it takes 10 minutes to process the records, nobody cared, because your program is the only program running.

In Windows, things are quite a bit different. Your program most likely is not the only program running. Remember, you've got Windows itself running, perhaps a Clock, and perhaps a Word Processing program. If you use the above Loop in a Windows Program, and it takes 10 minutes to process the records, your Program will be non-responsive to Windows Events for 10 minutes. Move another Window on top of your Application, and then move the other Window away, and your Application will be left with a "hole" on top of it, because it is not responding to a Paint Message that Windows sends to your program to tell it to repaint itself.

Under Windows, a better approach would be to process the Records in a Timer Event. Example:

Open Window Event Handling

Set(MyDataBase)

Timer Event Handling

Loop 10 Times
   Next(MyDataBase)
   If ErrorCode()
     Break
   End
End
Note that you prepare the Data File for processing when the Window first opens. You then process 10 records at a time in the Timer Event. Since Timer Events have lower priority than other Windows Events, your Program will be responsive to the User and to Events generated outside of itself (i.e., Windows telling your Program to re-paint itself).

If you break your processing of Serial Port data into "chunks", and use the Timer Event to process the data, you will be able to simultaneously handle 2 or more Serial Ports at the same time, without too much degradation. It should be noted that you can't just take the DOS version of the Loop and put it in a Windows Timer Event. Because the Accept Loop needs to Cycle to allow other Threads to run, if it takes 10 minutes to process data in a Timer Event, the Accept Loop will not cycle until that 10 minutes is up, and you will effectively lock out other threads.

NT Problems ====
Your program was, no doubt, compiled as a 16 bit EXE.

Windows NT barely tolerates 16 bit programs. If at all possible, they should not be used under NT.

The problem you are experiencing is due to the fact that the 16 bit NT Comm Driver does not support obtaining Modem Status. The 16 bit Comm API does not provide a means to obtain Modem Status. Microsoft provides an undocumented workaround which works fine in Win 3.x and Win 95/98. Apparently, however, Microsoft decided that Windows NT would not support this undocumented method of obtaining Modem Status.

PhoneMonitor and DialNumber both make a call to IsCd to determine if a Carrier with a remote modem has been established. IsCd, in turn, calls ModemStat to determine the current state of the Modem Status Register. The Modem Status Register provides a Bit Map that allows you to determine if there is a Carrier Signal. In a 16 bit program running under NT, ModemStat returns 0 which causes IsCd to return 0, which causes the Phone Monitor and DialNumber to fail to realize that a Connection has been established.

The only solution is to use the 32 bit Comm Driver which requires that your program be compiled as a 32 bit program using the 32 bit CLACom DLL.

Phone Monitor ====
Your program was, no doubt, compiled as a 16 bit EXE.

Windows NT barely tolerates 16 bit programs. If at all possible, they should not be used under NT.

The problem you are experiencing is due to the fact that the 16 bit NT Comm Driver does not support obtaining Modem Status. The 16 bit Comm API does not provide a means to obtain Modem Status. Microsoft provides an undocumented workaround which works fine in Win 3.x and Win 95/98. Apparently, however, Microsoft decided that Windows NT would not support this undocumented method of obtaining Modem Status.

PhoneMonitor and DialNumber both make a call to IsCd to determine if a Carrier with a remote modem has been established. IsCd, in turn, calls ModemStat to determine the current state of the Modem Status Register. The Modem Status Register provides a Bit Map that allows you to determine if there is a Carrier Signal. In a 16 bit program running under NT, ModemStat returns 0 which causes IsCd to return 0, which causes the Phone Monitor and DialNumber to fail to realize that a Connection has been established.

The only solution is to use the 32 bit Comm Driver which requires that your program be compiled as a 32 bit program using the 32 bit CLACom DLL.

Sending & Receiving Data ====
CLACom includes a Sample Program called GTERM. In addition to an already compiled program, you should find the source Applications for CFW 4 (ABC and Standard) and CFW 5 (ABC and Standard) in your CLACom installation directory. In the Public Files area of our Web Site you will also find many other example programs.

The CLACom Application Files contain a Wealth of Information. Nearly every line of Source Code that was written to interface CLACom with the application is commented with meaningful explanations. Most of the Communications Functionality in the GTERM Program comes from using the Templates (DialNumber, Terminal, AnswerPhone, etc).

It is difficult to offer Step By Step Instructions, since we have no idea what it is you wish to accomplish. You may wish to Dial a Number, Connect, and then send/receive a Credit Card authorization. Others may wish to Connect, and then send/receive several files. Some need to use the Serial Port to control a Robotic device (no modem attached, and precise control over the data stream is necessary).

The Basic Steps are as follows:

SetPort()
InitPort()
..... Your Code goes here
ResetPort()
We cannot possibly tell you what to do when it comes time to insert your own code to deal with the Serial Port Data Stream. All we can do is offer advice and suggestions.

If you need Terminal Emulation capabilities, by all means start with the GTERM Sample program as the "skeleton". Change the things you do not like and add your own features. All that we require is that you remove our Logo and Name from the About Box and Splash Screen.

If you need to send and receive files automatically, download the Client/Server Sample Application that is posted in the Software Library. It is fully commented and very easy to change to suit your own needs.

If you need any help implementing CLACom with your Clarion Application, need advice, want to know if it is better to use ComGetc() or ComGetd(), can't quite get that Modem to Dial, or whatever problem you may be having, please feel free to send an email to support@gapdev.com, and we will offer all the help we can. We would prefer E-Mail as opposed to Voice Calls on such issues because that allows us to send you snippets of code, and gives others an opportunity to share in the exchange of information.

To simultaneously monitor and process data on 2 or more Serial Ports, you need to use the Timer Event and process data in "chunks". Both of your "started" Procedures need to have a Window associated with them. This is because you need to do your work inside of an Accept Loop. The Windows, themselves, can be hidden if you have no need to display data.

In order for other Threads to run, you must CYCLE the Accept Loop. You do this by "falling through" to the End of the Accept Loop or by issuing an explicit Cycle command. If you need to import 100 or 1000 records that were received over the Serial Port, into a Database, no other Thread will run until you are finished importing the records. In 16 Bit Windows, no other Windows Program will run until you are finished.

The following is an Example of how things were done in DOS programs:

Set(MyDataBase)
Loop
   Next(MyDataBase)
   If ErrorCode()
     Break
   End
End
If there are 1000 records to process, the Loop would not terminate until an Error occurs or EOF is reached. If it takes 10 minutes to process the records, nobody cared, because your program is the only program running.

In Windows, things are quite a bit different. Your program most likely is not the only program running. Remember, you've got Windows itself running, perhaps a Clock, and perhaps a Word Processing program. If you use the above Loop in a Windows Program, and it takes 10 minutes to process the records, your Program will be non-responsive to Windows Events for 10 minutes. Move another Window on top of your Application, and then move the other Window away, and your Application will be left with a "hole" on top of it, because it is not responding to a Paint Message that Windows sends to your program to tell it to repaint itself.

Under Windows, a better approach would be to process the Records in a Timer Event. Example:

Open Window Event Handling

Set(MyDataBase)

Timer Event Handling

Loop 10 Times
   Next(MyDataBase)
   If ErrorCode()
     Break
   End
End
Note that you prepare the Data File for processing when the Window first opens. You then process 10 records at a time in the Timer Event. Since Timer Events have lower priority than other Windows Events, your Program will be responsive to the User and to Events generated outside of itself (i.e., Windows telling your Program to re-paint itself).

If you break your processing of Serial Port data into "chunks", and use the Timer Event to process the data, you will be able to simultaneously handle 2 or more Serial Ports at the same time, without too much degradation. It should be noted that you can't just take the DOS version of the Loop and put it in a Windows Timer Event. Because the Accept Loop needs to Cycle to allow other Threads to run, if it takes 10 minutes to process data in a Timer Event, the Accept Loop will not cycle until that 10 minutes is up, and you will effectively lock out other threads.

Step By Step Instructions ====
CLACom includes a Sample Program called GTERM. In addition to an already compiled program, you should find the source Applications for CFW 4 (ABC and Standard) and CFW 5 (ABC and Standard) in your CLACom installation directory. In the Public Files area of our Web Site you will also find many other example programs.

The CLACom Application Files contain a Wealth of Information. Nearly every line of Source Code that was written to interface CLACom with the application is commented with meaningful explanations. Most of the Communications Functionality in the GTERM Program comes from using the Templates (DialNumber, Terminal, AnswerPhone, etc).

It is difficult to offer Step By Step Instructions, since we have no idea what it is you wish to accomplish. You may wish to Dial a Number, Connect, and then send/receive a Credit Card authorization. Others may wish to Connect, and then send/receive several files. Some need to use the Serial Port to control a Robotic device (no modem attached, and precise control over the data stream is necessary).

The Basic Steps are as follows:

SetPort()
InitPort()
..... Your Code goes here
ResetPort()
We cannot possibly tell you what to do when it comes time to insert your own code to deal with the Serial Port Data Stream. All we can do is offer advice and suggestions.

If you need Terminal Emulation capabilities, by all means start with the GTERM Sample program as the "skeleton". Change the things you do not like and add your own features. All that we require is that you remove our Logo and Name from the About Box and Splash Screen.

If you need to send and receive files automatically, download the Client/Server Sample Application that is posted in the Software Library. It is fully commented and very easy to change to suit your own needs.

If you need any help implementing CLACom with your Clarion Application, need advice, want to know if it is better to use ComGetc() or ComGetd(), can't quite get that Modem to Dial, or whatever problem you may be having, please feel free to send an email to support@gapdev.com, and we will offer all the help we can. We would prefer E-Mail as opposed to Voice Calls on such issues because that allows us to send you snippets of code, and gives others an opportunity to share in the exchange of information.

Strings and Buffers ====
You are probably using "String Slicing" or "Array Indexing", as opposed to "String Concatenation", when storing Received Characters in your CSTRING buffer. Since Slicing and Indexing are faster than using String Operations, this is the preferred method of storing characters in a buffer. When clearing your String, you are no doubt using either the Clear() function or setting the string to an empty string (myCString = '').

The Clear() function and setting a string to an empty string work correctly with Clarion STRING's, however, they do not work as you might think with CSTRING's. With CSTRING's, Clarion simply inserts a NULL (0) in the 1st character position. Any other characters in the string, starting at position 2, are left intact.

If your Routine is not filtering out Carriage Returns and Line Feeds, these are being stored in your String along with the word BUSY. The next time in to your Routine, you begin storing Received Characters starting at position 1 (overwriting the NULL character and thus expanding the String). The problem is, the word BUSY is still in the String! Your call to StrWord() is finding the word BUSY and your Routine thinks that the Phone is Busy, when it actually is not.

CLACom includes a function called ClearVariable(). To use it, you pass the CSTRING variable, a 0, and the number of bytes to clear. For instance: ClearVariable(myCString,0,20). This will set the string to 20 Null's. Be careful that you do not attempt to clear more bytes than the string's declared length or you will overwrite memory and cause a General Protection Fault (GPF).

Templates ====
With Clarion 5.5 and above, you need to select the CLACom Template procedure from the Defaults Tab on the Select Procedure Type window.

CLACom is written in "C" and interfaces to your Clarion Application as a DLL. You are not required to use any of the Templates. They are simply provided to make certain Communications Tasks a bit easier (such as Dialing a Phone Number, Answering the Phone, etc).

If you would like to use the prewritten Template code, you can easily copy the code from the Template file and create your own Hand Written Procedures.

Terminal Emulator ====
The Terminal Emulator turns on Receive Notification to its Client Window. This means that the Terminal Window Procedure will always get first stab at any incoming characters. In 16 Bits, Windows sends a Notification message directly to the Window Procedure. In 32 Bits, CLACom starts a Background Thread that waits for Communications Events. When a character is received, this Thread sends a Notification message to the Window Procedure.

WaitForString will never "see" any characters while the Terminal Window is active. This is because the Terminal Procedure has already removed the characters from the Receive Buffer.

All is not lost however. You can temporarily disable Receive Notification to the Terminal Window, wait for a String to arrive, send your Response, and then re-enable Receive Notification.

This is similar to what the File Transfer routines do, since they need to have access to the Receive Buffer.

If AutoLogOn
   ! Don't let Terminal have any
   ! Characters until we have logged on
   CLATermPrepareTransfer(CLATermPort,0)
   ! Wait for User ID Prompt
   ! If String doesn't Arrive
   ! Display an Error Message
   ! and Close the Terminal Window
   If WaitForString(AppWin,CLATermPort,'USER ID',20)
      Do NobodyHome
      Post(Event:CloseWindow)
   Else
      ! We received the Log On Prompt
      ! Now send the User Id
      TmpCString = 'MyUserID^M'
      ModemPuts(CLATermPort, TmpCString)
      ! Clear any remaining characters
      ClrRecvBuf(CLATermPort)
      ! Re-Enable Notification Thread
      CLATermPrepareTransfer(CLATermPort,1)
   End
End

Timer Event ====
To simultaneously monitor and process data on 2 or more Serial Ports, you need to use the Timer Event and process data in "chunks". Both of your "started" Procedures need to have a Window associated with them. This is because you need to do your work inside of an Accept Loop. The Windows, themselves, can be hidden if you have no need to display data.

In order for other Threads to run, you must CYCLE the Accept Loop. You do this by "falling through" to the End of the Accept Loop or by issuing an explicit Cycle command. If you need to import 100 or 1000 records that were received over the Serial Port, into a Database, no other Thread will run until you are finished importing the records. In 16 Bit Windows, no other Windows Program will run until you are finished.

The following is an Example of how things were done in DOS programs:

Set(MyDataBase)
Loop
   Next(MyDataBase)
   If ErrorCode()
     Break
   End
End
If there are 1000 records to process, the Loop would not terminate until an Error occurs or EOF is reached. If it takes 10 minutes to process the records, nobody cared, because your program is the only program running.

In Windows, things are quite a bit different. Your program most likely is not the only program running. Remember, you've got Windows itself running, perhaps a Clock, and perhaps a Word Processing program. If you use the above Loop in a Windows Program, and it takes 10 minutes to process the records, your Program will be non-responsive to Windows Events for 10 minutes. Move another Window on top of your Application, and then move the other Window away, and your Application will be left with a "hole" on top of it, because it is not responding to a Paint Message that Windows sends to your program to tell it to repaint itself.

Under Windows, a better approach would be to process the Records in a Timer Event. Example:

Open Window Event Handling

Set(MyDataBase)

Timer Event Handling

Loop 10 Times
   Next(MyDataBase)
   If ErrorCode()
     Break
   End
End
Note that you prepare the Data File for processing when the Window first opens. You then process 10 records at a time in the Timer Event. Since Timer Events have lower priority than other Windows Events, your Program will be responsive to the User and to Events generated outside of itself (i.e., Windows telling your Program to re-paint itself).

If you break your processing of Serial Port data into "chunks", and use the Timer Event to process the data, you will be able to simultaneously handle 2 or more Serial Ports at the same time, without too much degradation. It should be noted that you can't just take the DOS version of the Loop and put it in a Windows Timer Event. Because the Accept Loop needs to Cycle to allow other Threads to run, if it takes 10 minutes to process data in a Timer Event, the Accept Loop will not cycle until that 10 minutes is up, and you will effectively lock out other threads.

Unresolved External ====
There are two developer files, in addition to the CLACom32.DLL, that must remain synchronized. These are:

  • clacom.inc
  • clacom32.lib
  • clacom32.dll
If one or the other is out of sync, you will receive linker errors. Whenever you download a new version or restore from a backup, be sure that you reference the 3 files from the same release. For instance a version 6.0 clacom.inc will not work with a version 6.1 clacom32.lib.

USB ====
CLACom works with USB Serial Devices such as Modems and Serial Printers, or any Device where the USB Port on the Device is the equivalent to a Serial Port.

Keep in mind that CLACom only talks to Serial Devices via the Windows Communications API. It knows nothing about the Physical Devices that you attach to a Serial or USB Port.

The USB Device will install a Device Driver that supplements the Windows Communications Driver. A USB Modem Driver, for instance, will add a Phantom Serial Port such as COM 3 or COM 4. You would then use this Serial Port when communicating with the Device.

Version Number ====
The most reliable method is to right click on the CLACOM32.DLL file and select Properties. On the Version Tab (Detals Tab in Vista) you will find the Compile Date and Version Number.

The screen should look similar to the following (click for a larger view):

Alternatively, the File Modification Date on any of the DLL files should show a Date and Time that represents the Compile Date and the Version Number.

Voice Lines ====
For this task, you cannot use the DialNumber Template Procedure, since that routine expects to connect with a remote modem.

Please see the Phone Dialer Application, available in the Software Library, for an example program that demonstrates the techniques of working with Voice Modems.

Depending upon the capabilities of the Modem that is doing the Dialing, you have several options. Basically, you simply use ModemPuts() to send the Dial String. Make sure there are at least 2 tildes ('~') after the Dial String ('ATDT226-8033^M~~'). CLACom will send the Dial String, and then wait 1 second before returning. Upon returning, the Modem will have Dialed the Phone Number.

Now is where you run into problems and what you do from here on out is dependent upon the Modem's capabilities.

The very basic approach is to tell the Operator to pick up the Phone, wait a second, and then send a Carriage Return (ASCII 13) to the Modem to tell it to stop trying to Connect. But what do you do if the Operator doesn't pick up the Phone in 1 second? By sending the Carriage Return to the Modem, the Modem will release the line and stop trying to Connect. If the Operator hasn't picked up an Extension Phone, the Line will be dropped and the Operator will receive a Dial Tone. You could wait for 2 seconds before telling the Modem to drop the Line. This might work, and gives the Operator 2 seconds to pick up the Handset. But then again, what if it takes the Operator 3 seconds to pick up the Handset? 4 or 5 Seconds? If your program just blindly sends a Carriage Return to tell the Modem to stop trying to connect, you've lost control over the human intervention - the Operator using the program.

Generally, once you have sent a Dial String to the Modem, the Modem expects to wait 50 or 60 seconds for a Carrier from a Remote Modem. You can stop your Modem from waiting by sending any character (normally a Carriage Return) to your Modem. If a Handset is not picked up after the Modem has Dialed a Number, then the Modem will release the Line all by itself when it has either Timed Out or you send a Carriage Return to it to tell it to abort the Call.

The Modem is only being used as an interface to the Phone System in order to send the DTMF Dial Tones. Once the Modem has Dialed a Number, the Modem can be "released". However, before releasing the modem, Somebody has to engage the Line by picking up an Extension Phone to keep the line Open.

The best approach is to use Modems that return Call Progress status (i.e., BUSY, VOICE, etc). When implementing an Automatic Dialer to do Sales Calls or any task where you are trying to Dial a Human, as opposed to another Modem, what you should be most concerned with is the "VOICE" response from the Modem. Not all Modems support a "VOICE" return code. USRobotics Modems do, some Rockwell Chip Set Modems do, but most do not.

When using a Modem to Dial a Voice Number in order to get a Human on the other end, you need to look for the word "VOICE" from your Modem. If you receive BUSY, CONNECT, or you time out, there is no sense telling the Operator of your Program to pick up the Phone and start talking.

Assuming you have a Modem (or block of Modems) that return the Result Code of "VOICE" if a Human answers the Phone, then after sending the Dial String to Dial a Number, you need to Monitor the incoming Serial Port Data Stream for the word "VOICE". You would "time out" after 60 seconds if nothing comes in, and abort the call if CONNECT comes in (which means you connected to another Modem), or you receive a "BUSY" string.

If your Modem does not return the "VOICE" Result Code, then simply Dial the Number, tell the Operator to "Pick Up the Handset", and send a Carriage Return to the Modem to tell it to Disconnect. Many programs provide an OK Button for the Operator to click on, to tell the program that the Handset has been picked up (the program tells the Modem to drop the Line only after the button has been pushed).

Also, have you noticed that extra Phone Jack on the back of the Modem? There should be 2 jacks. One labeled "Line" and one labeled "Phone". If you plug a Phone into the "Phone Jack", and pick up the attached Phone after the Modem has dialed a Number, the Modem should automatically release itself from the Phone Line. This is what this jack is provided for.

WaitForString ====
The Terminal Emulator turns on Receive Notification to its Client Window. This means that the Terminal Window Procedure will always get first stab at any incoming characters. In 16 Bits, Windows sends a Notification message directly to the Window Procedure. In 32 Bits, CLACom starts a Background Thread that waits for Communications Events. When a character is received, this Thread sends a Notification message to the Window Procedure.

WaitForString will never "see" any characters while the Terminal Window is active. This is because the Terminal Procedure has already removed the characters from the Receive Buffer.

All is not lost however. You can temporarily disable Receive Notification to the Terminal Window, wait for a String to arrive, send your Response, and then re-enable Receive Notification.

This is similar to what the File Transfer routines do, since they need to have access to the Receive Buffer.

If AutoLogOn
   ! Don't let Terminal have any
   ! Characters until we have logged on
   CLATermPrepareTransfer(CLATermPort,0)
   ! Wait for User ID Prompt
   ! If String doesn't Arrive
   ! Display an Error Message
   ! and Close the Terminal Window
   If WaitForString(AppWin,CLATermPort,'USER ID',20)
      Do NobodyHome
      Post(Event:CloseWindow)
   Else
      ! We received the Log On Prompt
      ! Now send the User Id
      TmpCString = 'MyUserID^M'
      ModemPuts(CLATermPort, TmpCString)
      ! Clear any remaining characters
      ClrRecvBuf(CLATermPort)
      ! Re-Enable Notification Thread
      CLATermPrepareTransfer(CLATermPort,1)
   End
End

Working with 2 or more Ports ====
To simultaneously monitor and process data on 2 or more Serial Ports, you need to use the Timer Event and process data in "chunks". Both of your "started" Procedures need to have a Window associated with them. This is because you need to do your work inside of an Accept Loop. The Windows, themselves, can be hidden if you have no need to display data.

In order for other Threads to run, you must CYCLE the Accept Loop. You do this by "falling through" to the End of the Accept Loop or by issuing an explicit Cycle command. If you need to import 100 or 1000 records that were received over the Serial Port, into a Database, no other Thread will run until you are finished importing the records. In 16 Bit Windows, no other Windows Program will run until you are finished.

The following is an Example of how things were done in DOS programs:

Set(MyDataBase)
Loop
   Next(MyDataBase)
   If ErrorCode()
     Break
   End
End
If there are 1000 records to process, the Loop would not terminate until an Error occurs or EOF is reached. If it takes 10 minutes to process the records, nobody cared, because your program is the only program running.

In Windows, things are quite a bit different. Your program most likely is not the only program running. Remember, you've got Windows itself running, perhaps a Clock, and perhaps a Word Processing program. If you use the above Loop in a Windows Program, and it takes 10 minutes to process the records, your Program will be non-responsive to Windows Events for 10 minutes. Move another Window on top of your Application, and then move the other Window away, and your Application will be left with a "hole" on top of it, because it is not responding to a Paint Message that Windows sends to your program to tell it to repaint itself.

Under Windows, a better approach would be to process the Records in a Timer Event. Example:

Open Window Event Handling

Set(MyDataBase)

Timer Event Handling

Loop 10 Times
   Next(MyDataBase)
   If ErrorCode()
     Break
   End
End
Note that you prepare the Data File for processing when the Window first opens. You then process 10 records at a time in the Timer Event. Since Timer Events have lower priority than other Windows Events, your Program will be responsive to the User and to Events generated outside of itself (i.e., Windows telling your Program to re-paint itself).

If you break your processing of Serial Port data into "chunks", and use the Timer Event to process the data, you will be able to simultaneously handle 2 or more Serial Ports at the same time, without too much degradation. It should be noted that you can't just take the DOS version of the Loop and put it in a Windows Timer Event. Because the Accept Loop needs to Cycle to allow other Threads to run, if it takes 10 minutes to process data in a Timer Event, the Accept Loop will not cycle until that 10 minutes is up, and you will effectively lock out other threads.

#######

Thanks for visiting GAP Development
code development in Dana Point, California