pcampb@shiva.trl.oz (Peter Campbell) (08/22/90)
This is a repost - our news server has been dead lately, I didn't even see this article when I sent it. Apologies to anyone it made it to. Thanks to all who sent answers re checking if printers are connected to a machine. All answers unfortunately fell into the category of BIOS functions, but I finally managed to get a hold of the MS-DOS encyclopedia and managed to work out the way to do it in DOS. Thanks especially to Clarence Dold & Lance Bresee. Here is the BIOS version (in C) from Clarence Dold, et.al: (See below for comments) > On my AT-Clone, with an Epson RX-80, I find that status truly boils down > to three bits: NOT_BUSY, PAPER_OUT, and IOERR > We could say that there are six states for the printer: > 1- No Printer I/O Card 0x00 > 2- Printer not connected 0x30 > 3- Printer Off 0x98 > 4- Printer Offline 0x18 > 5- Printer Out of Paper 0xb0 > Offline and out of paper 0x38 > 6- Online and ready. 0x90 > In each case, BIOS INT 0x17, ah=2 returns ^ > > I didn't bother with checking if BUSY was really BUSY with a print task, > since we were talking about a 'pre-print' on-line check. > I also don't know how shared printer usage affects the return. I suspect > that it would give 'off line' in either case. > > /* for QuickC, CADold cdold@tsdold.Convergent.COM */ > > #include <stdio.h> > #include <dos.h> > union REGS regs; > > #define PRT_BIOS 0x17 > #define PRT_STAT 0x02 > #define PRT_PORT 0x00 > #define NOTBUSY 0x80 > #define PAPER_OUT 0x20 > #define IOERR 0x08 > > main() > { > > int prt_stat, retstat=1; > regs.h.ah = PRT_STAT; > regs.x.dx = PRT_PORT; > int86(PRT_BIOS, ®s, ®s); > prt_stat=regs.h.ah; > printf("Port Status: %x\n", prt_stat ); > > switch(prt_stat & ( NOTBUSY | PAPER_OUT | IOERR) ) { > /* masking off unintelligent bits */ > case 0x00: > printf("No Printer Port\n"); > break; > case 0x08: > printf("Printer is Offline\n"); > break; > case 0x20: > printf("Printer is not connected\n"); > break; > case 0x28: > case 0xa0: > printf("Printer is Out of Paper\n"); > break; > case 0x80: > printf("Printer is Online and Ready\n"); > retstat=0; > break; > case 0x88: > printf("Printer is Turned Off\n"); > break; > } > void exit(retstat); > } /* end of main */ > This function makes use of the standard BIOS call with Interrupt 17H (H hex, given in PRT_BIOS) and function 02H (PRT_STAT) - get printer status. Seeing as this is a fairly standard BIOS function for (most) IBMs, and IBMs being what they are, it won't work. (I have a great respect for IBMs - a great respect for their proof of Murphy's Laws.) To get most printers to respond correctly you have to actually send a character to the printer. I have found that if you use function 01H (initialise printer) it just initialises a device, sending nothing to the printer really, and may stuff up anything you've done to use a non-standard printer. The printer can be turned off and the return status will be "o.k.". Function 02H seems to use the PREVIOUS printer call to work out if the printer is working. Hence, to get the above working you should make the following changes: #define PRT_STAT 0x00 /* Send byte to printer */ #define CAR_RET 0x0D /* Carriage Return */ ... regs.h.al = CAR_RET; This sends a <carriage return> to the printer, so the printer has to actually do something and you can be assured of getting some response in general. The above sort of thing can be done in Turbo C using the predefined function biosprint - see the reference guide if you've got it. However (you knew this was coming, didn't you :-), if you have a non-standard BIOS the above may simply not work (but you'd have to be pretty unlucky). The main trouble is that only Epson and the standard (if there is one) IBM printer are guaranteed to produce all of the above error messages (there may be a few others, don't sue me, but I don't know of them). Hence, for example, you may get an 'IO Error' from some printers when it's out of paper. Some printers will only give you the 'IO Error' message no matter what the error is, so it's best to take the error messages with a grain of salt. Furthermore, this sort of thing might not work with people using OS/2, as it is BIOS specific. Hence, try the following changes. This uses the DOS interrupts to send a character to the printer. Unfortunately the return error codes aren't nearly as specific for those of the BIOS call, even for standard Epson/IBM printers. For example, most errors like Timeouts and occasionally Printer Out of Paper will give a write fault on most machines. This function uses the predefined handle 4, which is set to the default printer (e.g. prn, usually lpt1). Unfortunately, to get at the return error message you have to get at the critical error handler, i.e. what is invoked if an interrupt 24H occurs. This is what puts up messages like "Drive not ready" when your printer is turned on, for instance. The reason it does this is because DOS uses the EXACT same type of DEVICEs for printers, files, drives, etc., and can not distinguish between them. For Borland's Turbo C at least, the above routine can be adjusted as follows: #include <stdio.h> #include <dos.h> union REGS regs; struct SREGS sregs; #define PRT_DOS 0x21 #define WRITE_DEVICE 0x40 #define HANDLE 0x04 /* Predefined Handle 4 - Printer */ #define CHAR_NUM 0x01 /* 1 Character to be sent to printer */ #define IGNORE 0 #define RETRY 1 #define ABORT 2 int handler(int errval, int ax, int bp, int si) /* This is where the actual error reporting is done */ { switch (errval) { case 0x00: printf("Disk write protected\n"); break; case 0x02: printf("Drive, Network or Printer not ready\n"); break; case 0x09: printf("Printer out of paper\n"); break; case 0x0a: printf("Write fault or Printer Timeout\n"); break; case 0x0b: printf("Miscellaneous Read Fault\n"); break; case 0x0c: printf("General Failure\n"); break; default: printf("Unexpected Error\n"); /* like CRC error, etc. */ break; } return(ABORT); /* Simple option - could give choice depending upon error */ } main() { harderr(handler); /* Tell program what to use for error handler */ unsigned char cr='\xD'; /* Carriage Return */ #define ptr (char far*)&cr;/* cast cr to get segment and offset */ int anerror, retstart; regs.h.ah = WRITE_DEVICE; regs.h.bl = HANDLE; regs.h.cl = CHAR_NUM; regs.x.dx = FP_OFF(ptr); /* address offset of cr */ sregs.ds = FP_SEG(ptr); /* address segment of cr */ /* int86x(PRT_DOS, ®s, ®s, &sregs); - Can use this, but might as well use: */ int ret; ret = intdosx(®s, ®s, &sregs); /* DOS interrupt 21H */ /* If carry flag is set, then error occurred */ anerror=(regs.x.cflag ? ret : 0) switch (anerror) { case 0x00: printf("No Error reported"); retstart=0; break; default: printf("An Error has been reported\n"); retstart=1; break; } void exit(retstat); } /* end of main */ This should work for all memory models, but my C is a bit rusty, plus I transferred it from prolog. I've been told that Timo Salmi has a copy of a Pascal or Turbo Pascal function which does the same thing as above sitting on chyde@uwassa.fi in Finland (IP # 128.214.12.3) that you can get at using anonymous ftp - I think it was in /pc/pd2 somewhere, but don't quote me. No, I can't remember the name of the file. The Turbo/PDC Prolog version is given below, so stop here if C is all you want. The Prolog version below also has the added extra of having the printer timeout length changed. The addresses changed are BIOS, so I didn't give them above. You also won't be able to find any word of them in any DOS manual - these come from the Norton Guides which a friend of mine has. Normal timeout values are set at $20, which, depending upon the machine used, can be anywhere from 20-100 seconds. ------------------------------------------------------------------------ /* The following is all Turbo/PDC Prolog code until otherwise stated. */ /* Feel free to cannabalise, flog, or otherwise use any of the code given below, unless Borland or PDC say otherwise. */ %% Written by PKC 6/8/90 - Prolog upsets registers, so can't test DOS % interrupts properly without redefining critical error handler global database - printer_check determ printer_error %% PKC 8/8/90 asserted if critical error global predicates criticalerror(integer,integer,integer,integer) - (i,i,i,o) language c % %% PKC 6/8/90 Critical Error Handler, as defined User Guide p.441 % criticalerror(integer, % (i) ErrNo % integer, % (i) ErrType % integer, % (i) DiskNo % integer) % (o) Action % - language c predicates % Give String for error number error_type(integer, % (i) Error Number string) % (o) Error Type % Assert database predicate to indicate error has occurred. assert_action(integer) % (i) Action - assert d/b flag if 0 = abort % Report type of error & ask user to cancel, retry or (maybe) ignore report_error(integer, % (i) General Error Number integer) % (o) Action (0 abort, 1 retry, 2 ignore) clauses % Give String for error number error_type(0,"Disk Write Protected"):-!. error_type(2,"Drive, Network or Printer not ready"):-!. error_type(9,"Printer out of Paper"):-!. error_type(10,"Write Fault or Printer Timeout"):-!. error_type(11,"Miscellaneous Read Fault"):-!. error_type(12,"General Failure"):-!. error_type(_,"Unexpected Error"):-!. % Assert database predicate to indicate error has occurred. % Only used for printer testing. assert_action(0):-assertz(printer_error),!. assert_action(_):-!. % Report type of error & ask user to cancel, retry or (maybe) ignore report_error(ErrNo,Action):- not(list_member(ErrNo,[0,2,9,10,11,12])),!, menu(10,10,30,15,["Abort operation", "Retry operation", "Ignore (not recommended)"], "An unexpected error has occurred", "ErrorARI",1,Choice), Action=Choice-1. report_error(ErrNo,Action):- error_type(ErrNo,ErrString),!, menu(10,10,30,15,["Abort operation", "Retry operation"], ErrString,"ErrorAR",1,Choice), Action=Choice-1, assert_action(Action). report_error(_,0):-!. % Catchall - abort % Redefinition of the critical error handler criticalerror(ErrNo,_,_,Action):- retractall(printer_error), error_type(ErrNo,ErrString), not(ErrString="Unexpected error."), report_error(ErrNo,Action),!. criticalerror(_,_,_,0):-!, retractall(printer_error), assertz(printer_error). predicates % determine which media are attached to program determine_media(integer) % (o) Flag, 1 if printer error, 0 if O.K. constants timeout_length = $02 %% 9/8 Printer Timeout ~2 x seconds length given clauses % determine which media are attached to program determine_media(0):- % test for printer attached %% PKC 9/8/90 Cut down Printer timeout to about 2-10 seconds %% Resetting all lpt1-4 as user may have redirected printer. membyte($0,$478,Out1), membyte($0,$478,timeout_length), membyte($0,$479,Out2), membyte($0,$479,timeout_length), membyte($0,$47a,Out3), membyte($0,$47a,timeout_length), membyte($0,$47b,Out4), membyte($0,$47b,timeout_length), %% PKC 21/8 change to Int. 21 - i.e. DOS, not BIOS, for printer test %% Fn. AH=$40, predefined handle (printer) $04 %% CX=#bytes to write from address DS:DX (segment:offset data buffer) %% Send 1 character - carriage return - in string. %% Get address of string with prolog ptr_dword. TestString="\n", ptr_dword(TestString,DataSegment,DataOffset), bios($21,reg($4000,$04,$01,DataOffset,0,0,DataSegment,0),_), %% Reset lpt1 timeout to previous value membyte($0,$478,Out1), membyte($0,$479,Out2), membyte($0,$47a,Out3), membyte($0,$47b,Out4), not(printer_error),!. %% printer_error asserted by critical error if printer not working. determine_media(1):- retractall(printer_error),!. /* Here Endeth the Prolog code. You'll need to shove this in your code as appropriate. The above stuff I have in 2 include files, plus a separate global predicate file. You might need a goal as well :-} The menu function is a standard Turbo Prolog toolbox predicate, it just gives you a nice pop-up menu to choose an option. Any other non-standard predicate I have above you can guess what it does from the name, if there are any more. */ ------------------------------------------------------------------------ Final Word: Even the above won't do you too much good on some networks, as it is extremely difficult to know what the network will do. For example, I'm using a Sun PC-NFS Network, which has the 'feature' of not allowing you to send stuff to the printer from within a file using the standard BIOS or DOS calls. What actually happens is it checks the printer DEVICE, which is NOT the printer. Depending upon how I have my configuration file set up, it will not send the character to the printer until I quit the program, or 5 minutes after the command is issued. I can't see any way around this problem. By the way, to whoever it was who told me about those addresses that indicated whether a printer was connected or not, these only indicate the printer DEVICEs - this still won't tell you whether the printer itself it turned on, out of paper, etc. I think this is what PC-NFS uses. Disclaimer: I take no responsibility for anything any of the above does, I make no claims of anything, I only used trademarks to represent products & if I done something wrong I didn't mean to. Any statement to the affect that the above is needed only because of the fact that the Intel processor is crippled should not be construed as a statement which could be taken as libel. Plus, I wrote this in Parliament so this is under parliamentary privilege (so you can't sue me), and I am probably the 13th cousin 42 times removed from some diplomat or other, and hence am under diplomatic immunity. Further Disclaimer: I am actually a channel for a spirit of a DOS, C and Prolog programmer, hence if you wish to complain about any of the above don't tell me, complain to the spirit world. Disclaimer to anything else: None of the views above represent those of my employers, unless it would make them money :-). ---------------------------------------------------------------------- Peter K. Campbell | 2/M6 | Oz Ph.: 03 541 6751 Telecom Research Laboratories | Phone : + 613 541 6751 P.O. Box 249 | Fax : + 613 543 6026 Clayton 3168 | Email : p.campbell@trl.OZ.AU Victoria, Australia | ----------------------------------------------------------------------
ts@uwasa.fi (Timo Salmi LASK) (08/22/90)
In article <2102@trlluna.trl.oz> pcampb@shiva.trl.oz (Peter Campbell) writes: > >Thanks to all who sent answers re checking if printers are connected to >a machine. All answers unfortunately fell into the category of BIOS >functions, but I finally managed to get a hold of the MS-DOS >encyclopedia and managed to work out the way to do it in DOS. The list misses one of the simplest and most effective of the methods, that testing the IOResult of an empty write to printer in Turbo Pascal. If the printer default timeout is changed for the duration of the test, the test can be made very fast. The facilities for this are included in /pc/ts/tspas22.arc available by anonymous ftp from chyde.uwasa.fi. ................................................................... Prof. Timo Salmi (Moderating at anon. ftp site 128.214.12.3) School of Business Studies, University of Vaasa, SF-65101, Finland Internet: ts@chyde.uwasa.fi Funet: gado::salmi Bitnet: salmi@finfun