Alternate fax servers in APRO 4

Async Professional 3.x introduced the fax server components, which provide backend facilities to create fax jobs, schedule them, and transmit them using multiple fax lines.  With the advent of new APRO 4 features and technologies (not to mention dropping 16-bit limitations), the fax server components may be overkill for some applications.  This tech tip discusses alternate fax server solutions.

Fax server basics

A fax server is simply a mechanism for handling fax transmissions.  In some circumstances, a fax server will only receive faxes, others will only send faxes composed on the local machine, others will send and receive, and still others just provide a central location for faxing throughout a network.  For the purposes of this tech tip, we'll look at a fax server that sends and receives, and is network compatible.

Step 1, receiving faxes

Receiving faxes is the easiest part of the fax server that we're going to create. Create a new application and drop a TApdComPort, TApdTapiDevice, TApdReceiveFax and TApdFaxStatus component on the form.  The TApdFaxStatus component will give us a nice little dialog that shows the progress of received faxes.  The TApdReceiveFax component will negotiate the parameters for the fax protocol and receive the fax.  The TApdComPort component will be the conduit through which the fax data is received.  The TApdTapiDevice component will select the faxmodem, configure it, and give us a useful benefit, that we'll discuss later.

Fax reception is usually an either-or operation, either we're receiving faxes or we're not.  Drop a TCheckBox on the form; name it cbxReceiveFax and change the label to "Receive faxes".  Create the OnClick event handler for this check box and tell the TApdReceiveFax to wait for faxes, as shown in the following code example:

 

procedure TForm1.cbxReceiveFaxesClick(Sender: TObject);

begin

  if cbxReceiveFaxes.Checked then

    { start waiting for faxes }

    ApdReceiveFax1.StartReceive

  else

    { stop waiting for faxes }

    ApdReceiveFax1.CancelFax;

end;

 

void __fastcall TForm1::cbxReceiveFaxesClick(TObject *Sender)

{

  if(cbxReceiveFaxes->Checked)

    // start waiting for faxes

    ApdReceiveFax1->StartReceive();

  else

    // stop waiting for faxes

    ApdReceiveFax1->CancelFax();

}

 

This will tell the TApdReceiveFax to wait for incoming calls when the check box is checked, or to cancel waiting if it is not checked. 

Now for some cool stuff

The TApdReceiveFax watches the port for "RING" signals to indicate when a fax is incoming.  That means the TApdComPort must be open, which will prevent the port from being used by other processes.  Fortunately, there is a way to wait for incoming calls while still allowing other processes to access the port. TAPI gives us the ability to passively wait for the incoming calls (we've been doing it for years with the TApdTapiDevice, it was just brought over to the fax side in APRO 4).  Select the TApdReceiveFax that is on your form and take a look at the TapiDevice property.  By default, that is left blank; set it to the instance of a TApdTapiDevice that you have dropped on the form.  You now have passive answering capabilities.  When the TApdReceiveFax.StartReceive method is called, we'll tell TAPI that we are interested in incoming calls, and will answer the phone when we get the right number of ring messages (that's determined by the AnswerOnRing property).  While the TApdReceiveFax is waiting, other TAPI-enabled processes can access the port (such as the TApdSendFax which we'll deal with momentarily). 

Step 2: Sending faxes

Sending faxes is a little more complicated than receiving faxes, mainly due to acquiring the APF file to send, and the phone number to which it is sent.  For our purposes, we'll do it from a button-click event.  But, before we can get the APF and phone number, we should assemble the components.  Drop a TApdComPort, TApdTapiDevice, TApdSendFax and TApdFaxStatus component onto the form.  Change the ComPort properties of the TApdSendFax and TApdTapiDevice components to the TApdComPort that you just dropped on the form (most likely named ApdComPort2). Also, set the TApdSendFax.TapiDevice property to the TApdTapiDevice just dropped on the form as well.  This tells the TApdSendFax that we'll be using this TApdTapiDevice to gain access to this TApdComPort.

Drop a TButton on the form, name it btnSendFax and change the caption to something like "Send a fax". Since we're going to ask for a fax file to send, drop a TOpenDialog on the form also.  Create the button's OnClick event handler and add something like this:

 

procedure TForm1.btnSendFaxClick(Sender: TObject);

var

  PhoneNum : string;

begin

  { set the dialog's filter to find APF files }

  OpenDialog1.Filter := 'Fax files (*.APF)|*.APF';

  if OpenDialog1.Execute then begin

    { a file was selected, use it }

    ApdSendFax1.FaxFile := OpenDialog1.FileName;

    { pre-select the previous phone number (if any) }

    PhoneNum := ApdSendFax1.PhoneNumber;

    { get the phone number }

    if InputQuery('Send a fax', 'Phone number: ', PhoneNum) then begin

      { a phone number was entered, use it }

      ApdSendFax1.PhoneNumber := PhoneNum;

      { disable the button to prevent reentrancy }

      btnSendFax.Enabled := False;

      { send the fax }

      ApdSendFax1.StartTransmit;

    end;

  end;

end;

 

void __fastcall TForm1::btnSendFaxClick(TObject *Sender)

{

  // set the dialog's filter to find APF files

  OpenDialog1->Filter = "Fax files (*.APF)|*.APF";

  if (OpenDialog1->Execute()){

    // a file was selected, use it

    ApdSendFax1->FaxFile = OpenDialog1->FileName;

    // pre-select the previous phone number (if any)

    String PhoneNum = ApdSendFax1->PhoneNumber;

    // get the phone number

    if (InputQuery("Send a fax", "Phone number: ", PhoneNum)){

      // a phone number was selected, use it

      ApdSendFax1->PhoneNumber = PhoneNum;

      // disable the button to prevent reentrancy

      btnSendFax->Enabled = false;

      // send the fax

      ApdSendFax1->StartTransmit();

    }

  }

}

 

Assuming that the user clicked the "Send a fax" button, selected an APF file and entered a phone number, we're now sending a fax.  The TApdSendFax.OnFaxFinish event will be generated once the fax is finished, so add that event handler and hit some keys on your keyboard until you get something resembling this:

 

procedure TForm1.ApdSendFax1FaxFinish(CP: TObject; ErrorCode: Integer);

begin

  { show the result of the fax }

  ShowMessage('Fax finished: ' + ErrorMsg(ErrorCode));

  { re-enable the button so we can fax again later }

  btnSendFax.Enabled := True;

end;

 

void __fastcall TForm1::ApdSendFax1FaxFinish(TObject *CP, int ErrorCode)

{

  // show the result of the fax

  ShowMessage("Fax finished: " + ErrorMsg(ErrorCode));

  // re-enable the button so we can fax again later

  btnSendFax->Enabled = true;

}

 

This will show a message dialog indicating the result of the fax (you'll need to add the AdExcept unit to your uses clause for the ErrorMsg method). 

Step 3: Sending and receiving faxes

Now that we have some code that will send faxes, and some code that will receive faxes, we can put them together to do both.  The cool stuff we talked about earlier is going to get really cool now.  Run the project that we just created.  Check the "Receive faxes" checkbox, you'll be presented with the TAPI device selection dialog, select one of your TAPI devices and remember the one you selected.  Once you click OK of the TAPI device selection dialog, APRO will start passively waiting for incoming calls. Now, click the "Send a fax" button, select an APF file (you can use the APROLOGO.APF file in your APRO\Examples folder) and enter a phone number in the ensuing dialogs.  Once you've entered the phone number, you'll see the TAPI device selection dialog.  Select the same device that your using for the receive.  Once you select the device, we'll open it and send the fax.  When the fax is complete, you'll get the message dialog showing the result of the fax.  Click OK on that dialog, and we're still waiting for incoming faxes.

When sending and receiving together like this, both of the TApdSendFax.TapiDevice and TApdReceiveFax.TapiDevice properties must be pointing to different TApdTapiDevice instances, and both instances of the TApdTapiDevice must have the same SelectedDevice.  You can make this easier to handle by doing something like this:

 

procedure TForm1.FormCreate(Sender: TObject);

begin

  { select the device for the first TApdTapiDevice }

  ApdTapiDevice1.SelectDevice;

  { use that same SelectedDevice for the second TApdTapiDevice }

  ApdTapiDevice2.SelectedDevice := ApdTapiDevice1.SelectedDevice;

  Caption := 'Using ' + ApdTapiDevice1.SelectedDevice;

end;

 

void __fastcall TForm1::FormCreate(TObject *Sender)

{

  // select a device for the first TApdTapiDevice

  ApdTapiDevice1->SelectDevice();

  // use that same SelectedDevice for the second TApdTapiDevice

  ApdTapiDevice2->SelectedDevice = ApdTapiDevice1->SelectedDevice;

  Caption = "Using " + ApdTapiDevice1->SelectedDevice;

}

 

This will set both of the SelectedDevice properties to the same TAPI device, which will let us 'share' the port.  We can't really share the port; only one process can access the port at a time.  Our TApdReceiveFax will open the TAPI device (and acquire the port) when it is actively receiving a fax; when it is passively waiting for a call the TAPI device can be opened by the TApdSendFax when it is ready to send a fax.  To prevent conflicts, you'll need to make sure the TApdSendFax doesn't try to send a fax while the TApdReceiveFax is actively receiving one.  Since our little project is sending faxes based on a button-click, it's easy to disable the button when the TApdReceiveFax is actively receiving, and enable it when it's done:

 

procedure TForm1.ApdReceiveFax1FaxStatus(CP: TObject; First,

  Last: Boolean);

begin

  if First then

    { this is the first time the OnFaxStatus fired for this session, }

    { disable the "Send a fax" button }

    btnSendFax.Enabled := False;

  if Last then

    { this is the last time the OnFaxStatus fired for this session, }

    { enable the "Send a fax" button }

    btnSendFax.Enabled := True;

end;

 

void __fastcall TForm1::ApdReceiveFax1FaxStatus(TObject *CP, bool First,

      bool Last)

{

  if (First)

    // this is the first time the OnFaxStatus fired for this session,

    // disable the "Send a fax" button

    btnSendFax->Enabled = false;

  if (Last)

    // this is the last time the OnFaxStatus fired for this session,

    // enable the "Send a fax" button

    btnSendFax->Enabled = true;

}

 

Expansion points

The project that we just created is a good starting point for a simple fax server, but it can be expanded upon for greater capabilities without too much trouble.  The big limitation in our example is that you have to click a button to send a fax.  Wouldn't it be nice if it could send a fax when something is printed to the APRO fax printer driver?  On the surface, this would be pretty easy to implement.  Simply use the TApdFaxDriverInterface.OnDocEnd event to call the btnSendFaxClick method.  But what if a fax is being received when the print job ends?  To handle this, you'll need to develop a queuing system that will collect the name of the APF and the phone number, then start sending once the receive is complete.  Tech tips are supposed to be simple tips, so I won't go into all of the details of creating a queuing system here.  Take a look at the TapiFaxInt.zip archive on ftp://ftp.turbopower.com/pub/apro/demos/.  That example uses the Padding field of the TFaxHeaderRec to store the phone number.  When a fax is ready to be sent, it adds the phone number to the Padding field, then copies the file to a "Pending faxes" folder; when the OnFaxFinish event is generated, it copies the APF that was just sent to a "Sent faxes" folder, then checks the "Pending faxes" folder for the next fax to send.  You can expand upon that queuing system by creating APFs on remote stations, modifying the Padding to contain the phone number and copying the file to the "Pending faxes" folder. 

Conclusion

The fax server components in APRO 4 have their purpose, but sometimes something homemade will work better for a particular situation. This tech tip walked through how to set up a TApdReceiveFax to passively wait for incoming calls.  We also talked about how to send a fax while a TApdReceiveFax is passively waiting.  Then, we rambled on about some expansion points, until finally we got here.

This site is not affiliated, endorsed, or otherwise associated with the entity formerly known as TurboPower Software. The owners and maintainers of Aprozilla.com were merely meager employees of the aforementioned organization, providing this site out of the pure goodness of their collective hearts. SourceForge.net Logo

Last updated: July 22, 2003.