Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Druckerstatus (https://www.delphipraxis.net/90089-druckerstatus.html)

Christoph Frank 11. Apr 2007 13:15


Druckerstatus
 
Hallo zusammen,

habe mich nun fast durch die ganze DP gequält und nichts richtiges gefunden. Hat denn niemand eine neue Erkenntnis zum Druckerstatus?
In meiner OLE Anwendung drucke ich im Hintergrund. Ist der Drucker nicht eingeschaltet gibt's ein Problem (blablabla... Systemkonfiguration überprüfen). Diese Meldung hätte ich gerne vor dem Hintergrunddruck mit einer sinnvollen Message abgefangen, damit der Anwender noch die Möglichkeit hat seinen Drucker einzuschalten, Papier nachzulegen usw.
Nachdem der Anwender mit OK bestätigt, soll dann erst der Hintergrunddruck erfolgen, vorausgesetzt es klappt dann, ansonsten wieder Meldung.
Eigentlich müßte das was ich machen möchte doch ganz einfach sein MS merkt doch auch, daß was nicht stimmt, sonst käme ja die Meldung nicht!
Gruß Christoph

shmia 11. Apr 2007 13:47

Re: Druckerstatus
 
Die Funktion GetPrinter() mit dem Level 6 aufrufen. Dazu wird die Struktur
PRINTER_INFO_6 (in Unit WinSpool)benötigt.

Christoph Frank 11. Apr 2007 15:04

Re: Druckerstatus
 
Huch, hab sowas noch nicht gemacht, wie rufe ich denn so eine Funktion auf?

shmia 11. Apr 2007 15:09

Re: Druckerstatus
 
Siehe: http://www.delphipraxis.net/internal...ct.php?t=64627
In diesem Sourcecode wird mit Level 2 gearbeitet und mit Level 2 erhältst du weit mehr Infos
als du eigentlich brauchst.
Der Druckerstatus steckt im Feld "Status".

Christoph Frank 11. Apr 2007 16:32

Re: Druckerstatus
 
Also, hier geht es ja richtig in die Windows Programmierung. Ich arbeite mit Win98, Printer_Info_6 ist aber für Windows NT.
Habe ich das richtig verstanden, wenn ich die Printer_Info_2 verwende muß ich nur den Status abfragen? Doch was kann ich daraus interpretieren?
So ganz ist mir das noch nicht klar.
Hat Delphi selbst keine Möglichkeit?

shmia 12. Apr 2007 09:01

Re: Druckerstatus
 
Zitat:

Zitat von Christoph Frank
Habe ich das richtig verstanden, wenn ich die Printer_Info_2 verwende muß ich nur den Status abfragen? Doch was kann ich daraus interpretieren?

Die verschiedenen Statuswerte gibt es als symbolische Konstanten in der Unit WinSpool:
Siehe PRINTER_STATUS_PAUSED, PRINTER_STATUS_WAITING, ...
Zitat:

Zitat von Christoph Frank
Hat Delphi selbst keine Möglichkeit?

Nein. Die VCL behandelt das Drucken stiefmütterlich. Die Klasse TPrinter ist zum Drucken ja sehr
unzureichend und auch QuickReport ist voller Bugs.

Christoph Frank 18. Apr 2007 11:24

Re: Druckerstatus
 
Hallo Shima,

erst mal vielen Dank für Deine Hilfe. Leider komme ich mit meinem Problem trotzdem nicht weiter. Habe mich nun die ganzen letzten Tage damit rumgeschlagen. Wahrscheinlich reichen meine Kenntnisse dafür einfach noch nicht aus.


Delphi-Quellcode:
// WinWord starten
  try
    MSWord := CreateOleObject('Word.Application');
  except
    ShowMessage('Ich kann Microsoft Word nicht starten.');
  end;
// WinWord anzeigen nur für Testzwecke := true
  MSWord.Visible := false;

  MSWord.Documents.Open(FileName:=Verzeichnis + '\' + 'Standard-Serienbrief.doc');
  MSWord.ActiveDocument.MailMerge.OpenDataSource
  (Name:=Verzeichnis + '\' + 'SelAdressen.txt');

  MSWord.Selection.Font.Name := 'Times New Roman';
  MSWord.Selection.Font.Size := 12;
  if Anzahl <> 0 then
  begin
     MSWord.ActiveDocument.MailMerge.Destination := 0;
     MSWord.ActiveDocument.MailMerge.MailAsAttachment := False;
     MSWord.ActiveDocument.MailMerge.MailAddressFieldName := '';
     MSWord.ActiveDocument.MailMerge.MailSubject := '';
     MSWord.ActiveDocument.MailMerge.SuppressBlankLines := True;
     for I := 1 to Anzahl do
     begin
        MSWord.ActiveDocument.MailMerge.DataSource.FirstRecord := 1;
        MSWord.ActiveDocument.MailMerge.DataSource.LastRecord := -16;
     end;
     MSWord.ActiveDocument.MailMerge.Execute;
  end;


// Ausdrucken

               >>>>> Hier möchte ich gerne prüfen, ob der Drucker bereit ist <<<<<<
 
  MSWord.Application.PrintOut(Background := false,
                              Range := 0,
                              Item := 0,
                              Copies := 1,
                              PageType := 0);


//Dateien schließen
  MSWord.Documents.Close(SaveChanges := 0);

//MSWord schließen
  MSWord.Application.Quit;
  MSWord := Unassigned;
Vielleicht kannst Du mir mal eine kleinen Lehrgang erteilen oder sagen was ich alles machen muß.
Gruß Christoph

[edit=Matze][delphi]-Tags repariert. Mfg, Matze[/edit]

shmia 18. Apr 2007 13:07

Re: Druckerstatus
 
Du kannst eigentlich auf den Windows-Spooler vertrauen:
dieser sorgt dafür, dass Dokumente ausgedruckt werden, sobald der Drucker zum Druck bereit wird.
Dies funktioniert auch nach einem Reboot.

Du möchtest wahrscheinlich verhindern, dass ein Benutzer viele Druckjobs absetzt,
der Drucker aber nicht bereit ist.
Im Extremfall könnte so die Arbeiten von Stunden verlorengehen.

Dann wäre es doch ratsamer, nur zu Prüfen, wieviele Druckaufträge gerade im Spooler sind.
Sind mehr als 3 Druckaufträge vorhanden dann bringt dein Programm diese Meldung:
Zitat:

Es sind 4 Druckaufträge im Spooler für Drucker "HP Desk 1000".
Wollen Sie einen weiteren Auftrag hinzufügen ?
<Ja> <Nein>
Delphi-Quellcode:
var
   info :TPrinterInfo2Easy;
begin
   GetPrinterInfo2(druckername,info); // aus Code-Library; siehe Link aus früherem Posting
   if info.cJobs > 3 then
   begin
      // Meldung ausgeben
      if MessageDlg(Format('Es sind %d Druckaufträge im Spooler für Drucker "%s".'#13#10, [info.cJobs, info.PrinterName])+
         'Wollen Sie einen weiteren Auftrag hinzufügen ?', mtConfirmation, [mbOK, mbCancel], 0) = mrCancel then
         Abort;
   end;
Jetzt bleibt nur noch das Problem, den aktuell eingestellten Druckername von WinWord
zu ermitteln. Das wäre aber eine eigene Frage.

Christoph Frank 18. Apr 2007 16:22

Re: Druckerstatus
 
So, ich hab es mal versucht, aber irgend etwas paßt da wohl noch nicht.

Eigentlich will ich nur den Anwender darauf hinweisen seinen Drucker einzuschalten und hatte nun angenommen das über den Status zu erreichen.

Ich habe Dir einmal die Inhalte zusammengestellt:
Delphi-Quellcode:
    Info.ServerName     ''
    Info.PrinterName    'EPSON Stylus DX5000 Series'
    Info.ShareName      ''
    Info.PortName       'EPUSB1:'
    Info.DriverName     'EPSON Stylus DX5000 Series'
    Info.Comment        ''
    Info.Location       ''
    Info.SepFile        ''
    Info.PrintProcessor 'EPSON Color Print Processor(70)'
    Info.Datatype       'RAW'
    Info.Parameters     ''
    Info.Attributes     '2116'
    Info.Priority       '1'
    Info.DefaultPriority '0'
    Info.StartTime      '0'
    Info.UntilTime      '0'
    Info.Status         '0'
    Info.cJobs          '0'
    Info.AveragePPM     '0'

Info.Status und Info.cJobs sind immer gleich '0', ob der Drucker nun eingeschaltet ist oder nicht. Wie gesagt, ich möchte nur den Anwender dazu veranlassen seinen Drucker anzuschalten. Nach Bestätigung versuche ich den Ausdruck noch einmal, klappte es ist's gut, wenn nicht neue Message.

bitsetter 18. Apr 2007 22:20

Re: Druckerstatus
 
Moin,

der Info.Status ist bei mir auch immer gleich 0. Info.cJobs gibt die Anzahl der aktuellen Druckaufträge an, das funktioniert bei mir jedoch.

Folgender Code funktioniert zumindest bei mir unter Win98 mit meinem Drucker:
Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var
  Needed: DWORD;
  Device, Driver, Port : array[0..200] of char;
  hPrinter, hDeviceMode : THandle;
  Buffer : Pointer;
  hDevice: cardinal;
begin
  Printer.PrinterIndex := -1;
  Printer.GetPrinter(Device, Driver, Port, hDeviceMode);
  if WinSpool.OpenPrinter(@Device, hPrinter, nil) then
  begin
    GetPrinter(hPrinter, 2, nil, 0, @Needed);
    GetMem(Buffer, Needed);
    if GetPrinter(hPrinter, 2, Buffer, Needed, @Needed) then
      if TPrinterInfo2A(Buffer^).pPortName <> nil then
      begin
        hDevice:= CreateFile(PChar('\\.\'+TPrinterInfo2A(Buffer^).pPortName), GENERIC_READ,
        FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0);
        if hDevice <> INVALID_HANDLE_VALUE then
        begin
          CloseHandle(hDevice);
          Showmessage('Der Drucker ist online.');
        end
        else
          Showmessage('Der Drucker ist offline.')
      end;
    FreeMem(Buffer, Needed);
    WinSpool.ClosePrinter(hPrinter);
  end;
end;
Ob das immer funktioniert ist natürlich eine andere Sache. Der Code musste ansonsten wohl noch etwas überarbeitet werden.

Christoph Frank 19. Apr 2007 10:24

Re: Druckerstatus
 
Hallo zusammen,

wußte garnicht, daß ich mit dem Drucker so'ne Lawine lostrete. Anscheinend ist das Problem Drucker aber garnicht so trivial.
Hallo bisetter, habe Deine Routine mal bei mir eingebaut. Eine kleine Änderung mußte ich vornehmen:

if WinSpool.OpenPrinter(@Device, hPrinter, nil) then
geändert in
if WinSpool.OpenPrinter(Device, hPrinter, nil) then
sonst gibt es einen Fehler : Incompatible Typen: 'Array' und 'Char'.

Bei der entscheidenden Abfrage erhalte ich immer folgendes:
if hDevice <> INVALID_HANDLE_VALUE then

hDevice = '4294967295'
INVALID_HANDLE_VALUE = '4294967295'

also ist der Drucker immer offline. Was kann ich denn jetzt noch machen?

bitsetter 19. Apr 2007 23:24

Re: Druckerstatus
 
Hallo,

den Code habe ich jetzt mit dem selben Drucker unter WinXP getestet, dort funktionierte er jedoch auch nicht.
Das man mit CreateFile unter Win98 jedoch nicht unter WinXP auf den Drucker zugreifen kann, haben wohl auch schon andere Leute festgestellt.
Wenn der Code bei dir unter Win98 nicht geht, dann wird es wohl nicht mit allen Druckern funktionieren. :(

Olli 20. Apr 2007 00:52

Re: Druckerstatus
 
Interessanter als der Wert des Handles waere was GetLastError() zurueckgibt. Ich vermute mal, dass einer der Parameter CreateFile() nicht schmeckt, denn prinzipiell kann man Symlinks auf Geraete unter XP (und NT4/2000/2003 ...) sehr wohl per CreateFile() oeffnen. Uebrigens eine nette Analogie zu Linux' "/dev"-Verzeichnis ;)

Im uebrigen braucht man einen Printermonitor (ja, das heisst so) um einen Druckjob zu unterbinden (falls das wirklich das Ziel ist) - es sei denn das Drucken selber (und die Statusabfrage) soll aus deinem eigenen Programm heraus erfolgen. Nichtmal Druckerbenachrichtigungen bringen dich sonst weiter, nur ein Printermonitor.


Uebrigens ist die Sinnhaftigkeit des Druckerstatus umstritten und haengt sehr stark davon ab was der Drucker zurueckmeldet, bzw. zurueckmelden kann. Das muss nicht unbedingt immer sinnvoll sein.

bitsetter 20. Apr 2007 08:12

Re: Druckerstatus
 
Zitat:

Zitat von Olli
Interessanter als der Wert des Handles waere was GetLastError() zurueckgibt. Ich vermute mal, dass einer der Parameter CreateFile() nicht schmeckt, denn prinzipiell kann man Symlinks auf Geraete unter XP (und NT4/2000/2003 ...) sehr wohl per CreateFile() oeffnen. Uebrigens eine nette Analogie zu Linux' "/dev"-Verzeichnis ;)

Hallo,

Delphi-Quellcode:
ShowMessage(SysErrorMessage(GetLastError));
gibt bei mir WinXP die Nachricht "Das System kann die angegebene Datei nicht finden" aus.

Olli 20. Apr 2007 12:16

Re: Druckerstatus
 
Zitat:

Zitat von bitsetter
Delphi-Quellcode:
ShowMessage(SysErrorMessage(GetLastError));
gibt bei mir WinXP die Nachricht "Das System kann die angegebene Datei nicht finden" aus.

Wenn du - wie ich oben schon schrieb - Geraete als Dateien ansiehst, weisst du was dieser Fehler bedeutet. Der Name oder das Namensformat ist falsch ;)

Christoph Frank 20. Apr 2007 12:49

Re: Druckerstatus
 
Na, nun mal wieder zum Thema:
Keiner mehr da, dem noch etwas einfällt, was ich nun mit meinem Problem machen könnte?
Gruß Christoph

Olli 20. Apr 2007 12:56

Re: Druckerstatus
 
Zitat:

Zitat von Christoph Frank
Na, nun mal wieder zum Thema:
Keiner mehr da, dem noch etwas einfällt, was ich nun mit meinem Problem machen könnte?
Gruß Christoph

Wenn du es in deinem eigenen Programm machen willst, dann hast du die Loesung schon oben, wenn es in fremden Programmen gehen soll, brauchst du einen Printermonitor.

@bitsetter: Mich wuerde mal der Wert fuer TPrinterInfo2A(Buffer^).pPortName interessieren, den ihr bekommt.

Christoph Frank 20. Apr 2007 14:55

Re: Druckerstatus
 
Hallo Olli

in dem Feld steht bei mir 'EPUSB1:', dasselbe wie im Info.PortName. Ich habe einen EPSON Stylus DX5000 Series an USB1 angeschlossen.
Zu Deiner Antwort:

Ja, ich möchte die Lösung in meinem Programm vornehmen.
Nein, die obige Lösung bringt immer 'Drucker ist offline' weil die Vergleichswerte identisch sind.
:shock: Monitor??? , - mache Delphi erst seit 4 Monaten !!!

Gruß Christoph

Olli 20. Apr 2007 18:21

Re: Druckerstatus
 
Zitat:

Zitat von Christoph Frank
in dem Feld steht bei mir 'EPUSB1:', dasselbe wie im Info.PortName. Ich habe einen EPSON Stylus DX5000 Series an USB1 angeschlossen.

Lade dir mal bitte WinObj von Sysinternals runter und schaue unter \?? oder \GLOBAL??, ob du dort diesen Namen so findest. Ich finde den Doppelpunkt irgendwie eigenartig. Der suggeriert, dass es dort noch mit einem Pfad weitergeht. IMO muss der Doppelpunkt weg.

Zitat:

Zitat von Christoph Frank
Ja, ich möchte die Lösung in meinem Programm vornehmen.
Nein, die obige Lösung bringt immer 'Drucker ist offline' weil die Vergleichswerte identisch sind.

Dann brauchst du keinen Printermonitor. Das mit den Werten ist genau was ich sagte, es haengt deutlich vom Druckermodell und vom Anschlussport ab, ob die gemeldeten Statusinformationen auch der Wirklichkeit entsprechen. Es koennte also genausogut voellig normales Verhalten sein.

bitsetter 20. Apr 2007 21:09

Re: Druckerstatus
 
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:

Zitat von Olli
@bitsetter: Mich wuerde mal der Wert fuer TPrinterInfo2A(Buffer^).pPortName interessieren, den ihr bekommt.

Unter Win98 wo es funktioniert USBPRN01 und unter WinXP wo es nicht funktioniert USB001. Bei mir also ohne Doppelpunkt.

Mit dem Tool WinObj konnte ich jedoch keinen ähnlichen Namen finden, im Bild ist ein Auszug davon.

Christoph Frank 21. Apr 2007 10:14

Re: Druckerstatus
 
Hallo Olli,

Sysinternals bei Microsoft.com?
Ist das auch für Win98, ich habe nur Win98.
Gruß Christoph

Olli 22. Apr 2007 00:01

Re: Druckerstatus
 
Zitat:

Zitat von bitsetter
Unter Win98 wo es funktioniert USBPRN01 und unter WinXP wo es nicht funktioniert USB001. Bei mir also ohne Doppelpunkt.

Faszinierend. Sowas wie unter 98 haette ich fuer XP eigentlich auch erwartet. Zumindest gibt es dort keinen Doppelpunkt im Namen. Ich bin im Moment noch etwas verwirrt (und im Eierlikoerdelirium :mrgreen:). Werde mich mal zum Thema USB-Stack schlaumachen und mich dann melden. Ggf. braucht es eine Erinnerung, falls ich es vergesse.

OT: Auch ein WM-Fan, wie ich sehe?! Auch Eule-Leser? :stupid:

Zitat:

Zitat von Christoph Frank
Sysinternals bei Microsoft.com?

Ja, die wurden vor fast einem Jahr von MS geschluckt.

Zitat:

Zitat von Christoph Frank
Ist das auch für Win98, ich habe nur Win98.

Nein, leider nicht. Auf Windows 98 gibt es meines Wissens nach nichts vergleichbares zum Windows NT Object Manager. Der ist unter NT dafuer zustaendig, dass dem Win32-Subsystem die ueblichen DOS-Namen sessionweise zur Verfuegung stehen. Vor gut einem Jahr gab es dazu auch einen Artikel von Marcel van Brakel und mir im TDM.

shmia 23. Apr 2007 09:44

Re: Druckerstatus
 
Also ich versuch's nochmal :|
Man braucht nicht zu prüfen, ob der Drucker online und bereit ist; das erledigt schon Windows.
Man sollte nur prüfen, ob zu viele Druckaufträge im Spooler liegen.

Der Benutzer könnte ja auch einen Laptop haben. In der Dockingstation kann er normal drucken
da dort ein Drucker angeschlossen ist.
Unterwegs kann er nicht drucken, aber das ist kein Problem, der Spooler druckt, sobald der Rechner
in der Dockingstation ist.

Ansonsten:
Immer zuerst mit GetPrinter() den Level 6 abfragen, sollte das misslingen (Returnwert prüfen), dann erfolgt
ein Fallback auf Level 2.

Olli 23. Apr 2007 10:36

Re: Druckerstatus
 
Zitat:

Zitat von shmia
Also ich versuch's nochmal :|
Man braucht nicht zu prüfen, ob der Drucker online und bereit ist; das erledigt schon Windows.
Man sollte nur prüfen, ob zu viele Druckaufträge im Spooler liegen.

Das ist IMO in zweierlei Hinsicht eine Fehleinschätzung.
1. Manche Drucker haben genug Speicher um hunderte Seiten Text mit ein paar Fontinfos aufzunehmen. Sobald es also an den Drucker geschickt ist, waere damit deine Methode zum Scheitern verdammt.
2. Handelt es sich um einen Drucker welcher auf die eine oder andere Weise an einem Printserver angeschlossen ist, ist die Einschätzung ob die Druckerwarteschlange voll ist oder nicht garnicht so einfach zu treffen.

Christoph Frank 24. Apr 2007 12:39

Re: Druckerstatus
 
Hallo zusammen,

ich habe mir das mit dem 'EPUSB1:' noch einmal genauer angesehen.
In Drucker/Einstellungen/Details steht bei mir bei nicht eingeschaltetem Drucker:

EPUSB1: (USB-Schnittstelle nicht angeschlossen)

Bei eingeschaltetem Drucker:

EPUSB1: (EPSON Stylus DX5000)
Ich nehme an, daß bei seriellen oder parallelen Anschlüssen die o.g. Routine funktioniert, nicht aber bei Abschlüssen über USB. Vielleicht würde ich mit meinem Problem weiterkommen, wenn mir jemand sagen könnte, wie ich den Anschluß zusätzlich zu den eventuellen Druckermeldungen prüfen könnte. Es hat ja nicht jeder einen Drucker über USB angeschlossen. In meiner Anwendung müßte ich halt alle eventuell möglichen Anschlüsse überprüfen können.
Gruß Christoph

Olli 24. Apr 2007 12:50

Re: Druckerstatus
 
Okay, habe zufaellig, da ich mich mal wieder in Dtl. aufhalte, einen USB-Drucker. Wenn ich es schaffe, schaue ich da mal heute im Laufe des Abends rein. Ansonsten muss es warten. Limit ist in knapp 2 Wochen, dann geht's zurueck ;)

bitsetter 24. Apr 2007 18:28

Re: Druckerstatus
 
Hi,

Zitat:

Zitat von Christoph Frank
Ich nehme an, daß bei seriellen oder parallelen Anschlüssen die o.g. Routine funktioniert, nicht aber bei Abschlüssen über USB. Vielleicht würde ich mit meinem Problem weiterkommen, wenn mir jemand sagen könnte, wie ich den Anschluß zusätzlich zu den eventuellen Druckermeldungen prüfen könnte. Es hat ja nicht jeder einen Drucker über USB angeschlossen. In meiner Anwendung müßte ich halt alle eventuell möglichen Anschlüsse überprüfen können.
Gruß Christoph

wie du weiter oben sehen kannst, habe ich unter Win98 meinen Drucker auch an einen USB Anschluss angeschlossen und es funktioniert bei mir wunderbar.

Unter WinXP konnte ich mit diesem \\?\USB#Vid_04a9&Pid_1062#6013TT#{28d78fad-5a12-11d1-ae5b-0000f803a8c2} Wert testen ob mein Drucker offline ist. Den hatte ich mit Regmon herausgefunden, wenn ich den USB Stecker vom Drucker reingesteckt bzw rausgezogen hatte. Das ist übrigens einer der Werte den auch das Tool WinObj angezeigt hatte.

Zitat:

Zitat von Christoph Frank
ich habe mir das mit dem 'EPUSB1:' noch einmal genauer angesehen.
In Drucker/Einstellungen/Details steht bei mir bei nicht eingeschaltetem Drucker:

EPUSB1: (USB-Schnittstelle nicht angeschlossen)

Bei eingeschaltetem Drucker:

EPUSB1: (EPSON Stylus DX5000)

Bei WinXP unter Drucker und Faxgeräte steht Offline, wenn der Drucker nicht eingeschaltet wurde.


Alle Zeitangaben in WEZ +1. Es ist jetzt 21:00 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz