Архивы: по дате | по разделам | по авторам

Прямая печать из Windows

Архив
автор : Юрий Ревич   13.10.1998

Несмотря на то, что Delphi имитирует печать в текстовом режиме, вывод на бумагу будет осуществляться с помощью стандартных средств Windows... <...> Поэтому не стоит также пытаться переключать атрибуты печати с помощью ESC-последовательностей.

С. Орлик. Секреты Delphi на примерах

В приведенной цитате из книги, написанной специалистом, в компетентности которого сомневаться не приходится, и адресованной к тому же профессиональным разработчикам, речь идет о печати без использования объекта TPrinter (с использованием процедуры AssignPrn). Тем не менее, прямая печать из Windows возможна. Но сначала разберемся: а зачем она нужна?

В стандартном диалоге Print (откройте пункт меню "Файл/Печать..." в любой Win-программе) на верхней панели справа есть маленькое незаметное окошечко "Печать в файл". Ручаюсь, вы никогда этим не пользовались - а зря. Если поставить в этом окошечке галочку, то собственно печати и не будет: вместо нее в текущем каталоге возникнет файл с расширением PRN. Он содержит последовательность кодов символов (если документ текстовый) или координаты точек (если это рисунок), перемежаемые теми самыми ESC-последовательностями для управления принтером (или вовсе без оных - если это чистый текст). Как ни странно, в Windows нет (по крайней мере, я не нашел в OSR2 Rus) никаких упоминаний, что с этим файлом делать дальше. В DOS все просто - надо послать этот файл напрямую на принтер (в устройство PRN или LPT1, что то же самое). Таким образом там как бы реализовывалась отложенная печать. А в Windows? При попытке прямой печати путем, например, запуска Norton Commander (а также его аналогов) в окне и затем печати через F5 в устройство PRN вы получите сообщение типа "Путь не найден", "Access violation" или что-то в этом роде. Короче, Windows о таком способе печати понятия не имеет. Печать из DOS-приложений ставится в общую очередь и для реализации прямой печати приходится запускать чистый сеанс DOS, что, конечно, крайне неудобно. А вообще, такая вещь может пригодиться - сама по себе процедура прямой печати занимает куда меньше ресурсов компьютера, чем запущенный Word или тем более Photoshop в совокупности со штатной программой вывода на принтер, освобождая тем самым ресурсы для других программ.

Другая возможная область применения такой процедуры - если вдруг вам взбредет в голову по какой-то причине организовать печать из своего приложения, минуя средства Windows, скажем, для ускорения вывода на матричный принтер.

Надо сказать, что, обнаружив возможность осуществления печати способом, изложенным ниже, я и сам был изумлен, ведь известно, что Windows совершенно не переносит, когда ее пытаются обойти. Так и происходит, если вы попытаетесь по привычке (как DOS) вставить в Delphi-приложение ассемблерную процедуру с использованием функций BIOS. А вот прямой вывод в порт проходит - видимо, потому, что если прерывания BIOS переопределяются, то "железо" никуда не девается и адреса портов все те же.

Порт LPT содержит три регистра: регистр данных DR = Baseaddr, регистр состояния SR = Baseaddr+1, регистр управления CR = Baseaddr+2. Для LPT1 это будут соответственно 0378h, 0379h и 037Ah. Для организации печати необходимо сигнализировать принтеру, что данные уже есть (на короткое время установить в 1 линию Strobe, то есть бит номер 0 регистра управления: CR0), затем отследить готовность принтера к приему следующих данных (линия SR6) и только потом посылать их. Полностью процедура включает еще проверку регистра состояния - в противном случае можно ожидать готовности до бесконечности, если в принтере кончилась бумага или его просто кто-то выключил. Функция prn посылает байт a на принтер и возвращает 0, если все в порядке, или значение регистра состояния, если ошибка (см. листинг).

Разумеется, Baseaddr нужно либо заменить соответствующим шестнадцатеричным числом (0378h для LPT1), либо объявить глобальную переменную (типа Word) с таким именем, принимающую соответствующее значение.

С использованием этой функции я составил небольшую программку, которая посылает PRN-файл на принтер, а в случае ошибки выводит on-top окно с сообщением и просто прерывает свою работу. Вообще-то, так не положено - правильнее будет проанализировать причину ошибки и предпринять соответствующие действия (в обращении с принтером не принято сразу сдаваться при каких-то ошибках, надо некоторое время ожидать - вдруг бумага каким-то чудом загрузится сама!). Не сомневаюсь, что грамотный читатель сможет сам довести это до ума; если возникнут вопросы - я к вашим услугам. Замечу, что функция без всяких изменений работает и в DOS-программах на языке Turbo Pascal. Кроме того, таким способом можно осуществлять обмен данными через параллельный порт.

 

function prn(a:byte);
begin
asm
mov @result,0
mov dx,Baseaddr
inc dx
inc dx {регистр управления}
mov al,13 {такое число надо послать - начало строба}
out dx,al
dec al {сбрасываем младший бит}
out dx,al {конец строба - никаких задержек не требуется}
dec dx {регистр статуса}

@ms: in al,dx
mov ah,al
and ah,08h {ошибка принтера если 0}
cmp ah,0
je @mer
mov ah,al
and ah,10h {принтер off line если 0}
cmp ah,0
je @mer
mov ah,al
and ah,20h {нет бумаги, если не 0}
cmp ah,0
jne @mer
mov ah,al
and ah,80h
cmp ah,0
je @ms {принтер не готов к приему - спрашиваем снова, пока не будет готов}
mov al,a
out dx,al {принтер готов, посылаем байт}
jmp @mend
@mer: mov @result,al
@mend: nop
end;
end;


Я спросил Юрия о том, как поведет себя эта программа, если воспользоваться ею одновременно со штатной печатью Windows. Он ответил, что программа конфликтует с Windows - они пытаются печатать по очереди. Тем не менее, этот опыт может оказаться полезным, особенно если зарегистрировать такую программу в качестве стандартного обработчика файлов PRN. При конфликте "штатная" печать доминирует - так как, вероятно, использует прерывание. Что, впрочем, ей не помогает - принтер все равно сбивается. - Сергей Леонов.

Хвосты

© ООО "Компьютерра-Онлайн", 1997-2021
При цитировании и использовании любых материалов ссылка на "Компьютерру" обязательна.