top of page

Sage 100 Fehler bei Verwendung "ReportViewerViewModel" / Error while using "ReportViewerViewModel"

(alle Versionen/all versions)

Mittwoch, 18. Dezember 2024

Deutsch

Fehlermeldung/Situation

Mögliche Ursachen

Mögliche Workarounds

Verwendeter Quellcode / Zweck des Quellcodes

Alternativer Ansatz



Deutsch


Fehlermeldung/Situation

Die Klasse "ReportViewerViewModel" bietet eine Option, um Sage 100-Berichte im Sage ERP Applikationsserver zu rendern und dann entweder zu drucken oder als PDF-Datei zu exportieren etc.

Code-Beispiel siehe Verwendeter Quellcode / Zweck des Quellcodes.


Die Verwendung der Klasse "ReportViewerViewModel" erscheint auf den ersten Blick einfach!

In der Praxis ergeben sich jedoch einige "nervige Stolperfallen", die man kennen sollte.

Beispielsweise können folgende Fehlermeldungen erst zur Laufzeit ausschließlich in bestimmten Systemen auftreten, während in der Entwicklungsmaschine diese Fehler nicht reproduzierbar scheinen.


Beispiel Fehlermeldungen:

System.Drawing.Printing.InvalidPrinterException: Kein Drucker installiert.   bei System.Drawing.Printing.PrinterSettings.GetHdevmodeInternal(String printer)   bei System.Drawing.Printing.PrinterSettings.GetHdevmodeInternal()   bei System.Drawing.Printing.PrinterSettings.CreateDeviceContext(PageSettings pageSettings)   bei System.Drawing.Printing.PrinterSettings.CreateMeasurementGraphics(PageSettings pageSettings)   bei Sagede.Shared.ReportViewerControl.ReportViewerViewModel.ExportPdf(String fileName, Boolean open)   bei Sagede.Shared.ReportViewerControl.ReportViewerViewModel.Export(String fileName, Boolean handleAfterExport)  => RaiseError in ReportViewerViewModel.cs(line 2861)

Alternativ erscheinen Fehlermeldungen im Sage TraceLog wie:

Mindestens ein Fehler ist aufgetreten.,,   bei System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)   bei System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)   bei System.Threading.Tasks.Task.Wait()   bei System.Threading.Tasks.Parallel.ForWorker[TLocal](Int32 fromInclusive, Int32 toExclusive, ParallelOptions parallelOptions, Action`1 body, Action`2 bodyWithState, Func`4 bodyWithLocal, Func`1 localInit, Action`1 localFinally)   bei System.Threading.Tasks.Parallel.For(Int32 fromInclusive, Int32 toExclusive, Action`2 body)   bei Sagede.Shared.ReportViewerControl.WpfPrintUtils.GetPrintTickets(PrinterSettings[] printerSettings)

Weitere mögliche Fehlermeldung:

SynchronizationContext 'System.Windows.Forms.WindowsFormsSynchronizationContext' is set in server process, which is most likly a bug. This could create a lot of harm like deadlocks,
ree

Mögliche Ursachen

Wir vermuten aktuell, dass einige Probleme in Kundensystemen durch die Vorgehensweise des Sage Quellcodes der Klasse "Sagede.Shared.ReportViewerControl.ReportViewerViewModel" verursacht werden.

Sage ruft in der Methode "Sagede.Shared.ReportViewerControl.WpfPrintUtils.GetPrintTickets" die .net-Framework-Klasse "PrintTicket" auf, um Druckerinfos zu ermitteln.


Das Problem:

Je nach vorhandener Laufzeitumgebung unter Windows gibt es für das "Lokale Systemkonto" (localsystem) im Sage Dienst ein Zugriffsproblem beim Abfragen dieser Druckereigenschaften.

Siehe: Microsoft .net-API-Doku zu "PrintTicket".

Deutlicher Hinweis von Microsoft dazu:

ree
Classes within the System.Printing namespace are not supported for use within a Windows service or ASP.NET application or service. Attempting to use these classes from within one of these application types may produce unexpected problems, such as diminished service performance and run-time exceptions.

Eine weitere mögliche Problemursache ist eine weitere Vorgehensweise von Sage in der Klasse "Sagede.Shared.ReportingEngine".


Sage greift z.B. in der Methode "Render" auf die .net-Framework-Funktionen zur Windows-Forms-Synchronisation zu, was ggf. o.g. TraceLog-Fehler bezüglich "SynchronizationContext " auslöst, der u.U. nur als Warnung zu verstehen ist, denn je nach Kontextsituation der Zugriffe in Mehrbenutzerumgebung sind keinerlei wirklichen Probleme festzustellen, d.h. die Berichte bzw. PDF-Dateien werden trotzdem serverseitig korrket gerendert/erzeugt.

ree
ree

Siehe Microsoft .net "WindowsFormsSynchronizationContext".



Mögliche Workarounds

Aktuell besteht ein Workaround darin, statt "Lokales Systemkonto" (localsystem) einen "echten" Benutzer als Dienstkonto zu hinterlegen, der auf der Maschine über lokale Admin-Rechte verfügt.

Beispiel:

ree









Wichtige Ergänzungen:



  • Das Aktivieren des Schalters "Datenaustausch zwischen Dienst und Desktop zulassen" bei Verwendung "Lokales Systemkonto" hatte in unserem Fall keine Lösung für das Problem gebracht! Dieser Schalter hat diverse positive Auswirkungen, z.B. auf den verfügbaren Heap-Speicher, ändert aber natürlich nicht die Situation der Berechtigung des Dienstkontos.


  • Unter Umständen kann das Verändern der installierten Druckertreiber (Standarddrucker-Verwaltung, andere Druckertreiber) Einfluss auf die Situation haben. Das haben wir jedoch nicht weiter verfolgt, da dieser Lösungsansatz nicht praxistauglich scheint. Die Druckerkonfiguration ändert sich bei großen Kunden viel zu häufig und generell müssen die Anwender bzw. Endkunden die Möglichkeit haben, diesbezüglich einzustellen, was gewünscht ist.


  • ⚠️Generell ist die Verwendung der Klasse "ReportViewerViewModel" zu überdenken, wenn die Verwendung in Quellcode erfolgen soll, der sehr häufig in der Hauptarbeitszeit des Endkunden aufgerufen werden soll! Diese Druck-Klasse ist äußerst Performance-"hungrig" und kann bei schnellem Einsatz hintereinander in einer großen Iteration mit mehreren hundert Belegen auch schon mal einen Sage Applikationsserver "in die Knie zwingen".⚠️

    Ein alternativer Ansatz ist hier beschrieben:

    https://www.officium-inservio.com/sage-100-1/reportviewerviewmodel#viewer-mdxok36194


Verwendeter Quellcode / Zweck des Quellcodes

using ( var vm = new Sagede.Shared.ReportViewerControl.ReportViewerViewModel
                 {
                   ReportName = reportName ,
                   ReportNamedParameters = namedParameters ,
                   WaitForPrintCommand = true // u.a. Synchronous print execution - should always be 'true' for printing and exporting - see Sage example!
                 } )
{
  SetNamedParameterToSuppressStandardPrint( namedParameters );
 
  // Note, the actual report rendering will be done on the Application server side, not(!) necessarily on this machine!
  // The "Sagede.Shared.ReportViewerControl.ReportViewerViewModel" will perform a server-side call to render the report content.
 
  // The "Export" method of the "ReportViewerViewModel" handles the export into a file.
  // IMPORTANT: The file extension of the file determines which file type will be used.
  // See code of Sage ERP "ReportViewerViewModel.Export".
 
  // !!! IMPORTANT !!! When running in the Sage Application Server environment, using "mandator.ParentSession.ApplicationToken.Name" will trigger a Sage security problem.
  // I.e. because then the application token within Sage Application Server will be "System", but the report might only be allowed in "Abf" !
  // Thus, we must set the application id manually!
  // string applicationId = mandator.ParentSession.ApplicationToken.Name;
  string applicationId = Sagede.OfficeLine.Shared.ApplicationToken.Abf.Name;
 
  vm.PrintReport( endpoint: realTimeDataServerEndpointAddress ,
                  user: userName ,
                  password: userPassword ,
                  applicationId: applicationId ,
                  key: reportName ,
                  namedParameters: namedParameters ,
                  language: languageCodeForPrintEngine ,
                  isAsync: false ,
                  isInitial: true );
 
  if ( System.IO.File.Exists( exportFullFileName ) )
  {
    System.IO.File.Delete( exportFullFileName );
  }
 
  if ( !vm.Export( exportFullFileName ) )
  {
    throw new Exception( ... );
 
  return true;
}

⚠️Achtung: ⚠️

Die Methode "PrintReport" hat einen otpionalen Parameter "isInitial", der unbedingt auf "True" gesetzt sein muss.


Alternativer Ansatz

Wir empfehlen für aktuellere Sage 100 Versionen grundsätzlich , den Druck über die alternative Sage Klasse "Sagede.Shared.ReportingEngine.ReportEngine" auszulösen.

Dies wird in nachfolgendem Artikel näher beschrieben.



bottom of page