My company recently implemented Fax/400, which works well for AS/400 applications but doesn't provide an easy way to fax directly from a PC. A user can print from the PC to an AS/400 output queue by using a virtual printer but must execute a command on the AS/400 to fax the output. This is true of most AS/400 fax software. It would be nice to print from an application on a PC, have a window pop up on the PC that prompts the user for fax information, and then have the AS/400 fax the printed output automatically.
In this article, I'll show how to use the PC Support application program interfaces (APIs) to fax from a PC application transparently through an AS/400 using Fax/400. (With slight modifications you can apply this solution to any AS/400 fax software.) You'll be using PC Support APIs for the connectivity between the PC and the AS/400. By using the PC Support Virtual Print facility, you can print output from the PC to an output queue on the AS/400; you can then fax the output before it is printed.
Advanced Function Printing (AFP) drivers, which allow graphic output to be printed on an AS/400-connected printer, are included with OS/400 V2R3 for both Microsoft Windows and OS/2. PC print output directed to an AFP driver can be faxed using the Send Fax (SNDFAX) command, which is part of Fax/400. Other AS/400 fax software programs have similar capabilities.
Output queues can have a data queue attached to them. When a spooled file is placed on the output queue and the file becomes READY, an entry is placed on the data queue. With a PC Support API, you can write a program that runs on the PC and receives entries from the data queue. This entry can then be used to submit a job to the AS/400 to actually send the fax.
Follow these steps to make this happen:
1. Although it will not actually be printed, direct the print output of a PC program to an AFP printer.
2. Monitor the AS/400 data queue associated with the output queue using a PC program, so you know when the AS/400 has the document ready for printing.
3. Fax the output on the AS/400 by having the PC program submit a remote SNDFAX command to the AS/400.
Step by Step
First, the PC Support AFP drivers must be loaded on the PC. This example was tested with Windows 3.1 and Windows under OS/2. In each case, you'll need to select a printer driver to direct the PC printout to your fax software. A list of drivers is presented, including one for Fax/400.
In Windows 3.1, run the driver installation program (PCSETUPW) from the folder QIWSFL2 on the AS/400. OS/2 includes a similar driver installation program (PCSETUP2), which can be run from the QIWSOS2 folder on the AS/400. Select the Fax/400 printer driver from the list of drivers. The OS/2 driver installation program does not install the corresponding WINOS2 printer driver; it must be installed separately. The easiest way to install the driver is to load the Windows version of PC Support on the WINOS2 System and install the AFP driver as you would under Windows.
Once the AFP printer drivers are installed on the PC, create the necessary objects on the AS/400. The first object to be created is the data queue, which will receive an entry each time a spooled file is placed on the output queue. The length of the data placed on the queue is 164 bytes. Its contents are described in the IBM manual Guide to Programming for Printing and shown in 1. The data queue can be created by the following command:
Once the AFP printer drivers are installed on the PC, create the necessary objects on the AS/400. The first object to be created is the data queue, which will receive an entry each time a spooled file is placed on the output queue. The length of the data placed on the queue is 164 bytes. Its contents are described in the IBM manual Guide to Programming for Printing and shown in Figure 1. The data queue can be created by the following command:
CRTDTAQ DTAQ(QUSRSYS/PCFAX) + MAXLEN(164) + TEXT('PC Fax Data Queue')
Now you can create the output queue that will be used to fax. There is a data queue parameter on the Create Output Queue (CRTOUTQ) command that was included in V2R2 of OS/400 but was not documented. It is fully documented in V2R3. The output queue can now be created by the following command:
CRTOUTQ OUTQ(QUSRSYS/PCFAX) + TEXT('PC Fax Output Queue') + DTAQ(QUSRSYS/PCFAX)
Once the data queue and the output queue have been created, you can create the printer device with the following command:
CRTDEVPRT DEVD(PCFAX) + DEVCLS(*LCL) TYPE(*IPDS) + MODEL(0) AFP(*YES) + AFPATTACH(*WSC) PORT(0) + SWTSET(0) ONLINE(*NO) + FONT(011) + TEXT('PC Fax Printer Device')
Conceptually, this printer is a virtual printer. Although you have given the printer device a device class (DEVCLS) of local (*LCL) and a model number, attachment type, port, and switch setting, you have not actually attached it to a workstation controller. If the ONLINE parameter is set to *NO, the system will not try to vary it on at IPL time. The printer does not need to be varied on for this example to work. The device type must be set to *IPDS, and it's important to set the AFP parameter to *YES to correctly process graphic data.
The virtual printer configuration will need a printer file to direct the output to the correct output queue. The printer file will be connected to the PCFAX printer device and have an output queue of PCFAX. It's also necessary to give the printer file a device type (DEVTYPE) of *AFPDS. This printer file can be created with the following command:
CRTPRTF FILE(QUSRSYS/PCFAX) + DEV(PCFAX) DEVTYPE(*AFPDS) + TEXT('PC Fax Printer File') + OUTQ(QUSRSYS/PCFAX)
When you direct output from the PC to the AS/400 PCFAX output queue, an entry is placed on the PCFAX data queue. You can write a program on the PC that will receive an entry on the data queue and process it, but you still need the ability to have more than one PC attached to the AS/400. The program on the PC that receives data queue entries should only receive entries belonging to that PC's user.
A Complete PC Fax System
To solve this problem, you can write a simple CL program on the AS/400 that will split up the entries from the PCFAX data queue and place them on a data queue assigned to each PC user. This CL program, shown in 2, will receive an entry from the PCFAX data queue using the QRCVDTAQ API. This program should be submitted to a job queue as a never-ending program.
To solve this problem, you can write a simple CL program on the AS/400 that will split up the entries from the PCFAX data queue and place them on a data queue assigned to each PC user. This CL program, shown in Figure 2, will receive an entry from the PCFAX data queue using the QRCVDTAQ API. This program should be submitted to a job queue as a never-ending program.
You can locate the user profile that the printed output belongs to by saving the field beginning in position 23 of the received data queue entry. Check to see if there is a data queue for this user profile by calling the Check Object (CHKOBJ) command. If there is no data queue created yet, create one. The data queue entry will be sent to this queue by using the QSNDDTAQ API.
The only thing you need now is a PC program that can receive a data queue entry and process it. 3 shows an example of a C program that will receive a data queue entry, prompt the user for a phone number to fax to, then perform a Submit Remote Command on the AS/400 to actually fax the spool file using Fax/400.
The only thing you need now is a PC program that can receive a data queue entry and process it. Figure 3 shows an example of a C program that will receive a data queue entry, prompt the user for a phone number to fax to, then perform a Submit Remote Command on the AS/400 to actually fax the spool file using Fax/400.
The first action taken in the example C program is to receive an entry from the user's data queue. The important parameters on the QRCVDTAQ API are the data queue name, data, and length of data. The system name will be passed as an empty string that will use the default system name. The wait time is set to -1 to force QRCVDTAQ to wait indefinitely for an entry.
When passing data from the AS/400 to a PC, there must be a conversion of data from EBCDIC to ASCII, and vice versa. The PC Support APIs provide the most common way to do this with the QSETMODE API. The QSETMODE API can initialize the session so that all data is translated automatically.
Although this is the most common method, it is ineffective in this situation. The problem with using the QSETMODE API is that there is one field-the spooled file number-that is binary and should not be translated. If the QSETMODE API is attempted, the binary field is either translated or an error code is returned to the application. To get around this problem, receive the data in EBCDIC, convert the binary field, and then call another PC Support API- EBCTOASC-to translate the remaining data fields to ASCII.
The binary field must be manipulated for correct translation as shown in the highlighted code in 3. Numbers on an Intel CPU reverse the high- and low-order bytes within a word and the high- and low-order words within a double (or long) word, so the bytes must be moved around.
The binary field must be manipulated for correct translation as shown in the highlighted code in Figure 3. Numbers on an Intel CPU reverse the high- and low-order bytes within a word and the high- and low-order words within a double (or long) word, so the bytes must be moved around.
Once the data is translated, a command string can be built to submit the SNDFAX command. This string is then submitted to the AS/400 with the Submit Remote Command PC Support API (EHNSRSBM). A return message buffer and its length are also sent as parameters.
You now have a complete system that will allow you to fax from a PC program.
Window Dressing
This program can be enhanced with a Windows or Presentation Manager front-end graphical user interface (GUI). You may also wish to prompt for all the Fax/400 parameters and submit them as well. It would be very easy to use this example for a fax product other than Fax/400 just by changing the Submit Remote Command string.
PC Support contains a rich set of APIs that is functional and can be used for a great number of applications. If you want to go beyond the traditional connectivity methods for communicating between the AS/400 and a PC, you're limited only by your imagination and the amount of time you can afford to spend.
Fred Wiest is the director of Information Services for Grundfos Pumps Corporation, a U.S. manufacturing company and subsidiary of Grundfos Pumps International. He has more than 16 years of software development expertise in IBM midrange and PC environments. He may be contacted by Internet at
REFERENCES
Guide to Programming for Printing (SC41-8194, CD-ROM QBKA7702).
IBM AS/400 Printing II (Redbook GG24-3704).
PC Support/400: Application Program Interface Reference (SC41-8254, CD-ROM QBKA6102).
Fax at Your Fingertips
Figure 1 Format of Data Queue Entry for a Spooled File
Data Name Data Type Description Function CHAR(10) Identifies the function that created the data queue entry. The value for a spooled file is *SPOOL. Record Type CHAR(2) Identifies the record type within the function. The valid value is: 01-A spooled file that has a READY status is on the output queue. Qualified CHAR(26) Identifies the qualified job name that created the Job Name spooled file. CHAR(10) Job name CHAR(10) User name CHAR(6) Job number Spooled File CHAR(10) Identifies the spooled file on the Name output queue. Spooled File BINARY(4) Identifies the unique number of the spooled file Number on the output queue. Qualified CHAR(20) Identifies the fully qualified output Output Queue queue name. Name CHAR(10) Output queue name CHAR(10) Library of the output queue Reserved CHAR(56) Reserved
Fax at Your Fingertips
Figure 2 CL to Split PCFAX Data Queue Entries to Individual
/*==================================================================*/ /* To compile: */ /* */ /* CRTCLPGM PGM(XXX/FAX001CL) SRCFILE(XXX/QCLSRC) */ /* */ /*==================================================================*/ FAX001CL: PGM /*********************/ /* DECLARE VARIABLES */ /*********************/ DCL VAR(&RCVNAME) TYPE(*CHAR) LEN(10) VALUE(PCFAX) DCL VAR(&RCVLIB) TYPE(*CHAR) LEN(10) VALUE(*LIBL) DCL VAR(&SNDNAME) TYPE(*CHAR) LEN(10) DCL VAR(&SNDLIB) TYPE(*CHAR) LEN(10) VALUE(*LIBL) DCL VAR(&LENGTH) TYPE(*DEC) LEN(5 0) VALUE(164) DCL VAR(&WAIT) TYPE(*DEC) LEN(5 0) VALUE(-1) DCL VAR(&DATA) TYPE(*CHAR) LEN(164) /******************************/ /* RECEIVE A DATA QUEUE ENTRY */ /******************************/ LOOP: CALL PGM(QRCVDTAQ) PARM(&RCVNAME &RCVLIB &LENGTH + &DATA &WAIT) IF COND(&LENGTH *GT 0) THEN(DO) /***********************************************/ /* CREATE USER DATA QUEUE IF IT DOES NOT EXIST */ /***********************************************/ CHGVAR VAR(&SNDNAME) VALUE(%SST(&DATA 23 10)) CHKOBJ OBJ(&SNDNAME) OBJTYPE(*DTAQ) MONMSG MSGID(CPF9801) EXEC(DO) CRTDTAQ DTAQ(QUSRSYS/&SNDNAME) MAXLEN(164) TEXT('PC + FAX DTA QUEUE FOR USER' *BCAT &SNDNAME) + SENDERID(*YES) ENDDO /**********************************/ /* PLACE ENTRY ON USER DATA QUEUE */ /**********************************/ CALL PGM(QSNDDTAQ) PARM(&SNDNAME &SNDLIB &LENGTH + &DATA) ENDDO GOTO CMDLBL(LOOP) END: ENDPGM
Fax at Your Fingertips
Figure 3 C Program to Receive Data Queue Entry
/* Include header files */ #include#include #include #include "ehndq.h" #include "ehnsrapi.h" #include "dtxlat.h" /* Define global variables */ char QueueName[22]; char SystemName[9]=""; long Wait=-1; int SenderID; char Data[164]; long DataLength=sizeof(Data); char SenderInfo[36]; char SplNbr[7]; char Phone[20]; char Job[29]; char Message[2000]; char EmptyCmd[]="SNDFAX TO( ) FILE( ) JOB( ) SPLNBR( )"; char Cmd[sizeof(EmptyCmd)]; /* Begin program */ void main(int argc, char *argv[]) { int rc; int Done=0; char SplNbrBin[4]; char *p; /* Get queue name from the first command line parameter */ memset(QueueName, ' ', sizeof(QueueName)); memcpy(QueueName, argv[1], strlen(argv[1])); /* Get an entry from the data queue */ while(!Done) { rc=QRCVDTAQ(QueueName, SystemName, Wait, SenderID, Data, &DataLength, SenderInfo); if (rc != DQ_OK) { printf(" Receive data queue failed-%d ", rc); } else { /* Ask for the fax information */ printf(" Enter Fax Number: "); gets(Phone); memcpy(Cmd, EmptyCmd, sizeof(Cmd)); /* Get the file number then translate to ASCII */ SplNbrBin[3]=Data[48]; /* Order for Intel dword/long */ SplNbrBin[2]=Data[49]; SplNbrBin[1]=Data[50]; SplNbrBin[0]=Data[51]; ltoa((unsigned long)*SplNbrBin, SplNbr, 10); memcpy(&Cmd[90], SplNbr, strlen(SplNbr)); ebctoasc(Data, Data, sizeof(Data)); /* Fill in the rest of the AS/400 command */ memcpy(&Cmd[10], Phone, strlen(Phone)); memcpy(&Cmd[37], &Data[38], 10); memset(Job, ' ', sizeof(Job)); memcpy(&Job[0], &Data[32], 6); p=strchr(Job, ' '); *p++='/'; memcpy(p, &Data[22], 10); p=strchr(Job, ' '); *p++='/'; memcpy(p, &Data[12], 10); memcpy(&Cmd[53], Job, sizeof(Job)-1); /* Issue the submit remote command */ rc=EHNSRSBM(Cmd, NULL, Message, sizeof(Message)-1); if (rc != DQ_OK) { printf(" Submit Command Failed -%d ",rc); } else { printf(" Submit Command Successful "); } } /* if (rc == DQ_OK) */ } /* While !done */ /* Stop data queues and remote command */ QSTPDTAQ(SystemName); EHNSRSTC(); } /* End main */
LATEST COMMENTS
MC Press Online