Umstellung Druckroutine für Massendruck von Delphi auf .Net

Servus miteinander,

mit meinem aktuellen Delphi Code erstelle ich eine LL-Previewdatei mit allen Rechnungen, merke mit je Rechnung die Position im LL File und exportiere am Ende aus diesem LL-File alles was ich benötige: Alle Rechnungen werden mit einem eigenen Namen als PDF exportiert und ggfls. einem Archivsystem zugeordnet. Rechnungen mit Email Kennzeichen werden per Email versendet, Rechnungen mit Zugferd-Kennzeichen bekommen das Zugferd XML importiert, Rechnung mit Druckkennzeichen werden zum Drucker versandt etc. Das alles funktioniert, da ich weiß, an welcher Position was steht und ich mit llStgsysConvert jede einzelne Aufgabe ansteuern kann. Das komplette LL-File landet ebenfalls im Archiv.

Unter .Net haben wir einen andere Herangehensweise und anstelle von DefineVars und DefineFields verwenden wir ein Dataset und rufen Print() auf um das LL File zu erstellen. Aber hier bekomme ich keine Information zurück, wo sich was befindet, um im Anschluß daran den Export anzustoßen. Oder doch?

Ich behelfe mir aktuell damit, jede einzelne Rechnung als Dataset bereitzustellen, einmal zu exportieren (Print() ruft scheinbar auch immer den Previewdialog auf) und für die Folgeaufgaben wieder in mit Append in ein “Gesamt” Preview-File zusammenzuführen. Das kostet ersten viel Zeit und zweitens bleibt bei LL30 immer ein Rest alloziierter Speicher übrig, der nicht mehr freigegeben wird. Meine Routine ist dies nicht, die Speicher stehen lässt. Das habe ich mit dem Leistungsprofiler abgecheckt.

Nun mein Problem bzw. meine Frage:

Ich würde gern wieder auf PrintStart → [Drucke jede einzelne Rechnung, merke Position] - PrintEnd zurückgreifen, aber das LlCore der API kennt das Dataset offensichtlich nicht, denn ein Aufruf dieser Funktion ruft eine Exception aus.

            int nRet = (int)ioLLCorePrintStart.Invoke(ioCore,
                new object[]
                {
                    GetEnumValue(ioLlProjectValues, "List"),
                    psVorlagePfad,
                    nPrintMode
                });

Habe ich hier grundsätzlich einen Denkfehler und der Ansatz ist komplett falsch? Ich will vermeiden, das ich für die einzelnen Aufgaben jedes mal das LL File neu erstellen muss, was mit dem Delphi Code gut funktioniert hat. Wie weiße ich also der LlCore-Ebene das Dataset zu?

Und wie lößt Ihr so eine Aufgabe?

Danke

Andreas

Hallo Andreas,

in .NET lässt sich das ebenfalls bequem umsetzen. Die Voraussetzungen dazu sind, dass

  • die aktuelle Version LL31 eingesetzt wird,

  • die Rechnungen (also die eine LL-Datei, die alle Rechnungen beinhaltet) mittels AutoMasterMode.AsVariables erstellt werden. Weitere Infos dazu findest du hier und hier

und

  • beim Verarbeiten der Vorschau-Datei diese mittels oneJobTranslation:false geöffnet wird.

Eine auf diese Weise erstellte Vorschau-Datei enthält für jeden Master-Datensatz (also für jede Rechnung/Kunden/Empfänger) einen JobIndex. Auf diesen JobIndex lässt sich über die PreviewFile-Klasse gezielt zugreifen und die Vorschau-Datei entsprechend verarbeiten.

Der folgende Beispielcode erstellt aus einer Vorschau-Datei jeweils eine separate PDF-Datei für jeden einzelnen Job innerhalb der Vorschau-Datei:

//Vorschau-Datei öffnen, WICHTIG: oneJobTranslation = false!
using (PreviewFile pf = new PreviewFile(@"C:\temp\inv_merg.ll", false, @"C:\temp\", LlLanguage.Default, oneJobTranslation: false))
{
    int jobCount = pf.JobCount;   //Anzahl der Jobs in der Vorschau-Datei ermitteln

    for (int jobIndex = 0; jobIndex < jobCount; jobIndex++)
    {
        // Dateiname je JobIndex
        string outputPdf = $@"C:\temp\invoice_{jobIndex + 1}.pdf";

        // WICHTIG: JobIndexRange auf genau diesen Job setzen
        // Syntax: "PDF;Export.JobIndexRange=<von>-<bis>"
        // Hier: nur ein Job => jobIndex-jobIndex
        pf.ConvertTo(outputPdf, $"PDF;Export.JobIndexRange={jobIndex}-{jobIndex}");
    }

}

Bei dem automatischen Öffnen der Vorschau handelt es sich um die sogenannte “IncrementalPreview”. Die Vorschau wird mit Fertigstellung der ersten Seite bereits geöffnet und dann während des noch aktiven Druckvorgangs um die Folgeseiten ergänzt. Das lässt sich mittels Property deaktivieren:

LL.IncrementalPreview = false;

Grüße
Chris

1 Like

Danke Chris,

ich denke das hilft mir im wesentlichen weiter!

Andreas

Das scheint aber nicht zu funktionieren. Mein Code sieht hier so aus (wir haben einen Wrapper für die verschiedenen LL Varianten aufgebaut) :

            // Vorschau-Dialog unterdrücken
            ioIncrementalPreview.SetValue(ioListLabel, false, null);
            ioAutoShowResult?.SetValue(ioListLabel, false, null);

Der Preview kommt trotzdem immer als Dialog hoch.

Andreas

Ich habe den Printmodus auf “Export” setzen müssen, dann hat es gepasst. Aber das ist auch nicht korrekt. Ich habe festgestellt das der JobCount immer 1 ist. Das ist ja aber auch nicht korrekt, wenn ich eigentlich z.b.. 10 Aufträge zum drucken gegeben habe. Jetzt rätsel ich ein wenig darüber, was ich am versorgen des Dataset falsch mache

Wenn der JobCount() immer 1 liefert, dann liegt die Vermutung nahe, dass die Vorschau-Datei nicht mit dem AutoMasterMode.AsVariables erstellt wird. Der Code würde wie folgt aussehen:

                //Übergabe des DataSets an List & Label
                LL.DataSource = _commandSetDataProvider;

                //Setzen der Master-Tabelle
                LL.DataMember = "Orders";

                //Die "Orders" Master-Daten sollen als Variablen angemeldet werden
                LL.AutoMasterMode = LlAutoMasterMode.AsVariables;

                //Den Standard-Projektnamen setzen
                LL.AutoProjectFile = "inv_merg.lst";

                //Drucken
                LL.Print();

Bei Verwendung von des AutoMasterMode.AsVariables wird mit jedem neuen Master-Datensatz LL-intern automatisch ein LlPrintResetProjectState aufgerufen, was zum Zurücksetzen von Seitenzählen, Summen etc. führt. Und es muss die Version 31 von List & Label sein.

Anbei ein absolut minimiertes Code-Sample (C#, .NET 8), welches das Vorhaben exemplarisch umsetzt. Eventuell kann das Sample als Vorlage dienen.

CreateAndSplitPreview.zip (1,1 MB)

Hallo Chris!

Erstmal Danke für Deine Mühe. Mit Hilfe des Beispielcodes habe ich auch den Print() Aufruf so zum laufen bekommen, das er die Previewdatei erstellt und hier bei alle Rechnungen mit Seite 1 beginnen, wie erwartet. Ich hatte das ganze auch mit Export() gebaut. Funktioniert genauso.

Soweit so gut.

Was ich nicht hinbekommen, egal was ich und der Claude uns ausdenken, ist die Anzahl der Jobs in der Previewdatei. Die ist immer “1”. Der JobIndex ebenfalls.

Eine Überprüfung der gesetzten Flags vor dem Aufruf von Print() sagt mir, das ich eigentlich nix falsch mache:

Das Ergebnis: Vor Print(): AutoMasterMode=AsVariables, AutoDestination=Export, IncrementalPreview=False, AutoProjectFile=xxx.lst.

Dataset und Datamember sind ok und auch die Relations sind gesetzt und überprüft.

Jetzt wird es aber interessant: Ich frage den Jobcount unmittelbar vor dem Export ab. Der ist 1. Der Export mit den Parametern Export.JobIndexRange={pnJobIndex}-{pnJobIndex} funktioniert aber eigenartigerweise. Allerdings, wenn ich 500 Rechnungen im Preview habe dauert es ca 2-3 Minuten bis alle erstellt sind, aber bei 2500 Rechnungen mehr als 2 Stunden. Das macht mich etwas stutzig, ob da nicht doch ein Indexproblem ist.

Wir setzten aktuell noch aufs Framework 4.8. Aber das sollte nicht das Problem sein, oder?

Wenn das Thema so für das Forum nicht der richtige Ort ist, mache ich einen Case auf.

Andreas

Ergänzung: Das Problem mit dem Jobcount ist keins!. Ich habe bei meinem Aufruf nicht die korrekte Anzahl Parameter mitgegeben. Das ist gelöst.

Wen es interessiert:

     FALSCH: object oPrevFile = Activator.CreateInstance(ioPreviewFile,
             new object[] { psPreviewDatei, false });

     RICHTIG: object oPrevFile = Activator.CreateInstance(ioPreviewFile,
                new object[] { psPreviewDatei, false,
                               Path.GetDirectoryName(psPreviewDatei),
                               ioLlLanguageDefault, false });

Aber mein Zeitproblem mit großen LL - Dateien besteht dennoch. Gibt es hier noch einen Hinweis?

Danke

Andreas

Danke für die Entwarnung. Schön zu lesen, dass es nun funktioniert.

Bezüglich der Performance könnte es verschiedene Ursachen haben. Der erste Anlaufpunkt ist hier ein Debwin-Logfile (..\combit\LL31\Verschiedenes\Debwin4.exe). Die Date/Time-Spalte lässt sich dahingehend anpassen, dass die Zeit als Differenz zur vorherigen Debug-Ausgabe angezeigt wird:

Das sollte einen genauen Hinweis liefern können, wo bzw. ab wann es klemmt.

Da es bei geringeren Anzahl an Rechnungen nicht aufritt, kann ein generelles Problem vermutlich ausgeschlossen werden. Denkbar, dass mit steigender Anzahl verarbeiteter Rechnungen der Speicher knapp wird. Läuft die Anwendung als x86 oder x64 Anwendung? Hier könnte sich zusätzlich ein Blick in den Ressourcenmonitor von Windows/TaskManager lohnen (wie verhält sich der Speicherverbrauch, die Handles, die GDI-Objekte, die Schreibrate auf dem Datenträger, …).

Zusätzlich könnte das Verhalten geprüft werden in die Suche nach verfügbaren Druckern im Netzwerk abgeklemmt wird:

                LL.Core.LlSetOption(LlOption.NoPrinterPathCheck, 1);

Desweiteren könnte - wenn der Drucker innerhalb der LL-Datei irrelevant ist - die Druckerinformationen nicht in die Vorschau-Datei eingebettet werden und immer der lokale Standarddrucker gewählt wird:

                //LL_OPTION_FORCE_DEFAULT_PRINTER_IN_PREVIEW (176) /* default: false */
                LL.Core.LlSetOption(176, 1);

Sind ggf. noch andere Objekte im Report enthalten, die gerendert oder konvertiert werden müssen, beispielsweise Grafiken oder PDF-Objekte?

Sollte das keine Kenntnisse liefern, dann müsste sich das in der Tat mal unser Support Team genauer anschauen.