none
Zugriff auf Druckwarteschlange RRS feed

  • Frage

  • Hallo Leute,
    habe ich eine Möglichkeit, dass ich Dokumente, die an einen bestimmten Drucker gesendet werden auch an einem zweiten Drucker ausdrucken zu lassen?

    Konkret werden Lieferscheine von einem bestehendem Programm (nicht von mir) am Drucker 1 ausgedruckt.
    Nun möchte ich aber einen zweiten Drucker (gleiche Type) bestückt mit färbigem Papier danebenstellen, sodass ich
    sowohl ein Original auf Drucker 1 und einen "Durchschlag" auf Drucker 2 (färbig) erhalte.

    Das bestehende Programm hat leider dazu keine Einstellmöglichkeiten.
    Kann ich dafür die Druckerwarteschlange des einen Druckers abfragen und sofern ein Druckauftrag darin vorhanden ist, diesen in die Druckwarteschlange des zweiten Druckers kopieren?

    Oder gibt es dafür auch eine andere Lösung?

    Vielen Dank im Voraus
    Christian Tauschek


    Christian Tauschek

    Mittwoch, 1. Januar 2014 18:40

Antworten

  • Mir kommt die Frage sehr bekannt vor. Hattest Du die schon mal gestellt?

    Die Überwachung der Job-Queue an sich ist zwar kein Problem (steckt alles in winspool.drv, einer DLL), aber dann an die Daten zu kommen, ist normalerweise nicht vorgesehen. Ob es doch irgendwie geht, weiß ich nicht. Aber eigentlich lässt sich Dein Problem doch viel einfacher lösen:

    Richte einen virtuellen Drucker ein, der Dateiausgabe macht (z.B. PDF) und lass das Fremdprogramm darauf drucken. Das Ausgabeverzeichnis überwachst Du mit einem kleinen Programm von Dir, das dann auf die beiden realen Drucker ausgibt.

    Gruß,

    Winfried

    Donnerstag, 2. Januar 2014 10:03
  • Hallo,
    auf CodeProject gibt es ein projekt zum überwachen der Druckeraufträge. Dieses findest du hier. In VB.NET musst du dann nurnoch einen verweis auf die PrintQueueMonitor.dll setzen. Abonnieren kannst du die Events dann wie folgt:

    Dim pqm As New PrintQueueMonitor("DRUCKERNAME")
    AddHandler pqm.OnJobStatusChange, AddressOf pqm_OnJobStatusChange

    DRUCKERNAME muss der exakte Name sein, wie der Drucker heißt. Also mit allem drum und dran. In nachfolgendem Screenshot gelb hinterlegt:


    Wie auf de Screenshot zu sehen ist, erreichst du die Liste über die Systemsteuerung.

    Im Eventhandler kannst du dann den Job heraus finden:

    Dim pq As New PrintQueue(New PrintServer("\\TOM-PC"), "DRUCKERNAME")
    Dim job = pq.GetJob(e.JobID)
    Wirklich weiter bin ich ab hier auch nicht mehr gekommen. Eventuell schaffst du es noch die Druckerdaten heraus zu bekommen. Wie man die Bytes dann drucken kann erfährst du hier. Ein Konverter zwischen C# und VB.NET steht in meiner Signatur.

    Koopakiller [kuːpakɪllɐ] (Tom Lambert)
    Webseite | Code Beispiele | Facebook | Twitter | Snippets   C# ↔ VB.NET Konverter
    Markiert bitte beantwortende Posts als Antwort und bewertet Beiträge. Danke.

    Mittwoch, 1. Januar 2014 21:06
    Moderator
  • Hallo,
    soweit ich weiß ist JobStream immer nothing. Ich habe, als ich die Antwort geschrieben habe, auch schon einiges Probiert, musste aber noch etwas andrees machen weswegen ich dort dann nicht weiter machte.

    Nun habe ich nochmal recherchiert und probiert. Ich habe es leider auch nicht geschafft das es funktioniert. Beim weiteren überlegen ist es eigentlichauch logisch. Wir können nichtmal wissen in welchem Form der erste Drucker die Daten bekommt und ob der 2. dieses Format unterstützt.

    Ich an deiner Stelle würde es mit Winfrieds Idee probieren. Wenn du es aber weiter anders probieren willst, ich habe nch 2 andere Ideen wovon die Zweite in jedem Fall funktionieren sollte:

    1. Versuche die gesendeten Daten abzufangen, abe auf Hardwareebene. Also daas was über den Anschluss raus geht. Möglich sollte das sein, nur die komplexität ist sicher übertrieben hoch.
    2. Erstelle einen eigenen virtuellen Drucker. Ich habe das hier dazu gefunden. Ich habe es nicht probiert ob man es in .NET machen kann oder nicht, aber so bekommst du die gesamten Daten des Druckauftrags. Diese würde ich dann versuchen als Datei zu speichern und dann auf beiden Druckern zu drucken.

    Viel Glück noch mit deinem Problem.


    Koopakiller [kuːpakɪllɐ] (Tom Lambert)
    Webseite | Code Beispiele | Facebook | Twitter | Snippets   C# ↔ VB.NET Konverter
    Markiert bitte beantwortende Posts als Antwort und bewertet Beiträge. Danke.

    Samstag, 4. Januar 2014 19:34
    Moderator
  • Wenn man ohne weiteres an die Druckdaten beliebiger Druckjobs kommen könnte, wäre das auch eine Sicherheitslücke in Windows. Die Docs zur Print Spooler API liegen übrigens unter

    http://msdn.microsoft.com/en-us/library/windows/desktop/ff686807(v=vs.85).aspx

    Gruß,

    Winfried

    Sonntag, 5. Januar 2014 09:49

Alle Antworten

  • Hallo,
    auf CodeProject gibt es ein projekt zum überwachen der Druckeraufträge. Dieses findest du hier. In VB.NET musst du dann nurnoch einen verweis auf die PrintQueueMonitor.dll setzen. Abonnieren kannst du die Events dann wie folgt:

    Dim pqm As New PrintQueueMonitor("DRUCKERNAME")
    AddHandler pqm.OnJobStatusChange, AddressOf pqm_OnJobStatusChange

    DRUCKERNAME muss der exakte Name sein, wie der Drucker heißt. Also mit allem drum und dran. In nachfolgendem Screenshot gelb hinterlegt:


    Wie auf de Screenshot zu sehen ist, erreichst du die Liste über die Systemsteuerung.

    Im Eventhandler kannst du dann den Job heraus finden:

    Dim pq As New PrintQueue(New PrintServer("\\TOM-PC"), "DRUCKERNAME")
    Dim job = pq.GetJob(e.JobID)
    Wirklich weiter bin ich ab hier auch nicht mehr gekommen. Eventuell schaffst du es noch die Druckerdaten heraus zu bekommen. Wie man die Bytes dann drucken kann erfährst du hier. Ein Konverter zwischen C# und VB.NET steht in meiner Signatur.

    Koopakiller [kuːpakɪllɐ] (Tom Lambert)
    Webseite | Code Beispiele | Facebook | Twitter | Snippets   C# ↔ VB.NET Konverter
    Markiert bitte beantwortende Posts als Antwort und bewertet Beiträge. Danke.

    Mittwoch, 1. Januar 2014 21:06
    Moderator
  • Mir kommt die Frage sehr bekannt vor. Hattest Du die schon mal gestellt?

    Die Überwachung der Job-Queue an sich ist zwar kein Problem (steckt alles in winspool.drv, einer DLL), aber dann an die Daten zu kommen, ist normalerweise nicht vorgesehen. Ob es doch irgendwie geht, weiß ich nicht. Aber eigentlich lässt sich Dein Problem doch viel einfacher lösen:

    Richte einen virtuellen Drucker ein, der Dateiausgabe macht (z.B. PDF) und lass das Fremdprogramm darauf drucken. Das Ausgabeverzeichnis überwachst Du mit einem kleinen Programm von Dir, das dann auf die beiden realen Drucker ausgibt.

    Gruß,

    Winfried

    Donnerstag, 2. Januar 2014 10:03
  • Hallo Winfried,
    ja ich habe diese Frage schon mal gestellt und kenne deshalb auch die pdf-Lösung.

    Da aber der externe Programmanbieter die Software wartet und mir wieder hundert Fragen stellt (bzw. bei Problemen auf das "PDF-Verteil-Programm" verweist) wieso das Ganze auf einen virtuellen PDF-Drucker gedruckt und auf diesen die Druckformulare angepasst werden sollen, habe ich nach einer Möglichkeit gesucht, die unabhängig ist.

    So gesehen gefällt mir die Lösung, wie von Koopakiller dargestellt, ganz gut. Nur bin ich leider noch nicht dazugekommen diese zu testen, was ich aber in den nächsten Tagen mal machen möchte.
    Sollte ich das nicht hinbekommen, so werde ich das mit der PDF-Variante machen.

    mfg
    Christian


    Christian Tauschek

    Donnerstag, 2. Januar 2014 16:18
  • Hallo Tom,
    ich habe nun das Beispiel aus CodeProject zum Laufen gebracht und bekomme auch Events gefeuert, wenn ein Druckauftrag in die Warteschlange von Drucker1 eingeht.
    Die Events werden scheinbar nur dann gefeuert, wenn ich auf die Zielplattform x86 kompiliere. (mit x64 geht es nicht)

    Wenn auf Drucker1 ein Druckauftrag eingeht, dann wird der untenstehende Code aufgerufen und dort wird für Drucker2 ein neuer Job angelegt und dieser wird auch auf Drucker2 ausgedruckt.
    Das Problem ist nun, dass ich auf Drucker2 nur "Text auf der Kopie" (siehe Code unten) und nicht den Original-Druckauftrag von Drucker1 ausdrucken kann.

    Leider komme ich auch nicht an die Daten des Jobs in der Druckerwarteschlange von Drucker1 heran.
    Ich habe zwar Zugriff auf JobName, JobSize (988) und JobStatus (8216) jedoch JobStream ist immer Nothing.
    Aber es müsste doch eine Möglichkeit geben, um auf die Rohdaten des Druckauftrages von Drucker1 heranzukommen.

        Private Sub pqm_OnJobStatusChange(ByVal Sender As Object, ByVal e As PrintJobChangeEventArgs)
            Dim pqDrucker1 As New PrintQueue(New PrintServer("\\Christian-PC"), "Drucker1")
            Dim pqDrucker2 As New PrintQueue(New PrintServer("\\Christian-PC"), "Drucker2")
    
            Dim jobDrucker1 As PrintSystemJobInfo = pqDrucker1.GetJob(e.JobID)
    
            Dim myStream As Stream = pqDrucker2.AddJob(jobDrucker1.Name & " (Kopie)").JobStream
            Dim myByteBuffer As Byte() = UnicodeEncoding.Unicode.GetBytes("Text auf der Kopie")
    
            myStream.Write(myByteBuffer, 0, myByteBuffer.Length)
            myStream.Close()
        End Sub
    mfg
    Christian


    Christian Tauschek



    Freitag, 3. Januar 2014 22:36
  • Hallo Tom,
    ich habe nun das Beispiel aus CodeProject zum Laufen gebracht und bekomme auch Events gefeuert, wenn ein Druckauftrag in die Warteschlange von Drucker1 eingeht.
    Die Events werden scheinbar nur dann gefeuert, wenn ich auf die Zielplattform x86 kompiliere. (mit x64 geht es nicht)

    Wenn auf Drucker1 ein Druckauftrag eingeht, dann wird der untenstehende Code aufgerufen und dort wird für Drucker2 ein neuer Job angelegt und dieser wird auch auf Drucker2 ausgedruckt.
    Das Problem ist nun, dass ich auf Drucker2 nur "Text auf der Kopie" (siehe Code unten) und nicht den Original-Druckauftrag von Drucker1 ausdrucken kann.

    Leider komme ich auch nicht an die Daten des Jobs in der Druckerwarteschlange von Drucker1 heran.
    Ich habe zwar Zugriff auf JobName, JobSize (988) und JobStatus (8216) jedoch JobStream ist immer Nothing.
    Aber es müsste doch eine Möglichkeit geben, um auf die Rohdaten des Druckauftrages von Drucker1 heranzukommen.

        Private Sub pqm_OnJobStatusChange(ByVal Sender As Object, ByVal e As PrintJobChangeEventArgs)
            Dim pqDrucker1 As New PrintQueue(New PrintServer("\\Christian-PC"), "Drucker1")
            Dim pqDrucker2 As New PrintQueue(New PrintServer("\\Christian-PC"), "Drucker2")
    
            Dim jobDrucker1 As PrintSystemJobInfo = pqDrucker1.GetJob(e.JobID)
    
            Dim myStream As Stream = pqDrucker2.AddJob(jobDrucker1.Name & " (Kopie)").JobStream
            Dim myByteBuffer As Byte() = UnicodeEncoding.Unicode.GetBytes("Text auf der Kopie")
    
            myStream.Write(myByteBuffer, 0, myByteBuffer.Length)
            myStream.Close()
        End Sub

    mfg

    Christian


    Christian Tauschek

    Samstag, 4. Januar 2014 18:35
  • Hallo,
    soweit ich weiß ist JobStream immer nothing. Ich habe, als ich die Antwort geschrieben habe, auch schon einiges Probiert, musste aber noch etwas andrees machen weswegen ich dort dann nicht weiter machte.

    Nun habe ich nochmal recherchiert und probiert. Ich habe es leider auch nicht geschafft das es funktioniert. Beim weiteren überlegen ist es eigentlichauch logisch. Wir können nichtmal wissen in welchem Form der erste Drucker die Daten bekommt und ob der 2. dieses Format unterstützt.

    Ich an deiner Stelle würde es mit Winfrieds Idee probieren. Wenn du es aber weiter anders probieren willst, ich habe nch 2 andere Ideen wovon die Zweite in jedem Fall funktionieren sollte:

    1. Versuche die gesendeten Daten abzufangen, abe auf Hardwareebene. Also daas was über den Anschluss raus geht. Möglich sollte das sein, nur die komplexität ist sicher übertrieben hoch.
    2. Erstelle einen eigenen virtuellen Drucker. Ich habe das hier dazu gefunden. Ich habe es nicht probiert ob man es in .NET machen kann oder nicht, aber so bekommst du die gesamten Daten des Druckauftrags. Diese würde ich dann versuchen als Datei zu speichern und dann auf beiden Druckern zu drucken.

    Viel Glück noch mit deinem Problem.


    Koopakiller [kuːpakɪllɐ] (Tom Lambert)
    Webseite | Code Beispiele | Facebook | Twitter | Snippets   C# ↔ VB.NET Konverter
    Markiert bitte beantwortende Posts als Antwort und bewertet Beiträge. Danke.

    Samstag, 4. Januar 2014 19:34
    Moderator
  • Wenn man ohne weiteres an die Druckdaten beliebiger Druckjobs kommen könnte, wäre das auch eine Sicherheitslücke in Windows. Die Docs zur Print Spooler API liegen übrigens unter

    http://msdn.microsoft.com/en-us/library/windows/desktop/ff686807(v=vs.85).aspx

    Gruß,

    Winfried

    Sonntag, 5. Januar 2014 09:49
  • Hallo Winfried,
    das mit Argument mit der Sicherheitslücke ist eigentlich logisch. Daran habe ich gar nicht gedacht.

    Ich möchte es nun so machen wie von dir vorgeschlagen und einen Pfad auf eingehende PDF's überwachen und diese dann auf 2 Drucker verteilen.

    Soweit kann ich die PDF's auch schon zum Standarddrucker schicken.
    Jedoch weiß ich nicht wie ich beim untenstehenden Code einen bestimmten Drucker angeben kann ohne den Standarddrucker zu verwenden.

    mfg
    Christian

                Dim printProcess = New Process()
                printProcess.StartInfo.FileName = "C:\Users\Christian\Desktop\pdf\Dokument1.pdf"
                printProcess.StartInfo.UseShellExecute = True
                printProcess.StartInfo.Verb = "print"
                printProcess.Start()


    Christian Tauschek

    Sonntag, 5. Januar 2014 11:03
  • Teilweise kann man dem Programm noch weitere Parameter mitgeben, die den Drucker bestimmen. Andererseits würde sich vielleicht auch eine zusätzliche Bibliothek dafür lohnen. Ideen findet du hier und hier.

    Ich habe es mit

    Process p = new Process();
    p.EnableRaisingEvents = true; //Important line of code
    p.StartInfo = new ProcessStartInfo()
    {
        CreateNoWindow = true,
        Verb = "print",
        FileName = file,
        Arguments = "/d:" + "DRUCKER"
    };
    probiert. Mit dem Foxit Reader als Standardprogramm funktioniert es. Die Windows 8 Reader App spielt aber nicht mit.


    Koopakiller [kuːpakɪllɐ] (Tom Lambert)
    Webseite | Code Beispiele | Facebook | Twitter | Snippets   C# ↔ VB.NET Konverter
    Markiert bitte beantwortende Posts als Antwort und bewertet Beiträge. Danke.

    Sonntag, 5. Januar 2014 11:24
    Moderator
  • Hallo Tom,
    irgendwie ist der Computer seit gestern auf Kriegsfuß mit mir...

    Der untenstehende Code funktioniert zwar aber ich kann Drucker angeben was ich will der Ausdruck kommt immer beim Standarddrucker heraus.

    mfg
    Christian

    Dim printProcess = New Process()
    With printProcess
        .EnableRaisingEvents = True
        .StartInfo.CreateNoWindow = True
        .StartInfo.Verb = "print"
        .StartInfo.FileName = "C:\Users\Christian\Desktop\pdf\Dokument1.pdf"
        .StartInfo.Arguments = "/d:" + "Lexmark E352dn (MS)"
        .Start()
    End With


    Christian Tauschek

    Sonntag, 5. Januar 2014 13:49
  • Hallo Christian,
    entschuldige bitte, dass ich erst jetzt antworte. Ich hatte einiges zu tun und fand auch keine perfekte Lösung.

    Mein zuletzt geposteter Code ging bei mir bei einem HP Netzwerkdrucker und OneNote. Bei den anderen Druckern auch nicht. Ich fand nun einen Workaround, der bisher gut funktionierte. Leider muss dafür der Standarddrucker geändert werden:

    Sub Main()
        For Each printer As String In New String() {"Drucker 1", "Drucker 2"}
            SetDefaultPrinter(printer)
            Dim p As New Process()
            p.StartInfo = New ProcessStartInfo() With { _
                 .CreateNoWindow = True, _
                 .Verb = "print", _
                 .FileName = fileName _
            }
            p.Start()
            p.WaitForExit()
        Next
    End Sub
    
    <Runtime.InteropServices.DllImport("Winspool.drv")> _
    Public Function SetDefaultPrinter(printerName As String) As Boolean
    End Function

    Dabei wird nun der Standarddrucker gesetzt und anschließend gedruckt. Sobald das erste Dokument gedruckt wurde, wird der Drucker geändert und das zweite Dokument wird gedruckt.

    Es ist keine perfekte Lösung, aber sie scheint zu funktionieren.


    Koopakiller [kuːpakɪllɐ] (Tom Lambert)
    Webseite | Code Beispiele | Facebook | Twitter | Snippets   C# ↔ VB.NET Konverter
    Markiert bitte beantwortende Posts als Antwort und bewertet Beiträge. Danke.

    Sonntag, 12. Januar 2014 17:00
    Moderator
  • Hallo Tom,
    danke für deine Antwort.
    Ich hatte deine Lösung auch schon getestet. Das hat auch bei mir funktioniert.
    Nur muss eben vorher der Standarddrucker umgeschalten werden.
    Weiters öffnet sich bei *.pdf-Dateien immer der Adobe-Reader, welcher danach wieder geschlossen werden muss. Leider ist das alles - wie auch von dir angedeutet - keine saubere Lösung.

    Ich habe mir den Printer++ heruntergeladen und diesen als Drucker installiert.
    Bei diesem virtuellen Drucker kann man recht einfach einen Ausgabepfad für die PostScript-Dateien, die beim Drucken erzeugt werden, angeben.

    Diesen Pfad überwacht mein Programm und schiebt die eingehenden PostScript-Dateien direkt in die Druckwarteschlange der gewünschten Drucker.

    Das Ganze funktioniert wirklich gut und ist - so glaube ich zumindest - eine saubere Lösung.

    mfg
    Christian


    Christian Tauschek

    Sonntag, 12. Januar 2014 17:18