top of page

Sage 100 DCM werden nicht immer ausgelöst / DCM not always triggered

(alle Versionen/all versions)

Mittwoch, 5. Oktober 2022

Deutsch

Situation

Häufigkeit der Probleme

Wahrscheinliche Problemursache

Problematischer Sage Quellcode zur Abarbeitung der DCMs

Beispiel für Problemsituation

Aktuell bekannte Workarounds

Sage 100 Source code

DCM Source code


English

Situation

Frequency of problems

Probable cause of the problem

Problematic Sage source code for processing the DCM

Example for a problematic situation

Currently known workarounds

Sage 100 Source code

DCM Source code



Deutsch


Situation

Definierte Sage DLL-Common-Methods (DCM) werden zur Laufzeit nicht immer ausgelöst bzw. laufen bei der Ausführung auf Fehler, die nicht sofort auffallen.


Häufigkeit der Probleme

Das Problem betrifft grundsätzlich alle Sage 100-Versionen!

Wir haben bei einem unseren Kunden mal mit einer Kontrollfunktion "gemessen", wie oft z.B. eine Druck-DCM von Sage nicht zuverlässig ausgelöst wurde bzw. vollständig durchgelaufen ist.

Bei der Sage 100 Version 8.0 kam heraus, das ca. bei jedem 20. Druck eine Druck-DCM nicht ausgelöst wurde.

Ab der Sage 100 Version 8.1 Von 161 Druckvorgängen wurde die DCM 9 Mal nicht ausgelöst.

Ab der Sage 100 Version 9.0 haben wir zuletzt ebenfalls bei einigen Installation dasselbe Problem festgestellt , das Problem besteht also weiterhin.


Wir haben bei einem größeren Kunden zahlreiche DCM im Zusammenhang mit Verkaufsbelegen im Einsatz (größere Installation, circa 50 User).

Dort wurde ebenfalls protokolliert, wenn DCM-Aufrufe fehlgeschlagen sind (weil dann wichtige Objekte nicht gesetzt waren).

Das Protoktoll von uns ergab, dass im Laufe eines Jahres mehrere Duzend mal die DCM nicht ausgelöst bzw. im Sage internen Code für den DCM Dispatcher aufgrund von Exceptions vorzeitig abgebrochen wurden.

Durch den vorzeitigen Abbruch des DCM Dispatcher-Durchlaufes wurden z.B. DCMs , die in der Reihenfolge nach der Exception auszuführen gewesen wären , dann folglich nicht mehr getriggert.

Für Beispiel Quellcode für Reaktion auf eine DCM siehe DCM Source code.


Wahrscheinliche Problemursache

Wir vermuten , dass die Vorgehensweise von Sage bei der Abarbeitung der DCM die Probleme überhaupt erst zulässt , insbesondere, wenn bei einem Kunden mehrere DCM registriert sind, z.B. auch von verschiedenen Sage Techpartnern.

Es ist leicht möglich, dass Fehler bei der Verarbeitung in der Kette der DCM-Abarbeitung dazu führen, dass nicht der gesamte registrierte DCM Quellcode aller übrigen Tech Partner korrekt weiterverarbeitet wird.

Besonders problematisch dürfte die Situation bei Endkunden sein, die mehrere Anpassungen verschiedener Sage Techpartner im Einsatz haben, die alle auf dieselben DCM-Listener mit .net-Quellcode reagieren (siehe Beispiel weiter unten).


Eine ebenfalls wichtige Ursache ist, dass des Sage Code, der die eigentliche DCM intern bei Sage auslöst, vorher bereits auf irgendeinen Fehler laufen kann, d.h. der Sage Quellcode kommt zur Laufzeit gar nicht erst bis zu der Stelle an der die DCM ausgelöst werden.


Entscheidender "Startpunkt" für die DCM-Ausführung ist Sage intern die private Methode "Sagede.OfficeLine.Engine.Mandant.ExecuteDCM" am Mandanten-Objekt ("Mandant").

D.h. diverse Sage Assemblies rufen an entsprechenden Stellen im Quellcode letzten Endes diese Methode des Mandanten-Objekts auf, um die DCM-Ausführung zu triggern.

Nachfolgend ein Code-Beispiel aus dem disassemblierten Original Sage Quellcode der privaten Methode "Sagede.OfficeLine.Wawi.PrintEngine.Belegdruck.InitializeTempTables".


Siehe Sage 100 Source code.


Man sieht am Ende dieses Quellcodes sehr gut den DCM-Aufruf "Me.DCMCallBeleg(DcmListId.PrintEKInitTables, DcmListId.PrintVKInitTables)".

Sollte vor diesem Aufruf in dieser Methode (oder in der davor befindlichen) irgendeine Exception ausgelöst werden, so wird der Aufruf der DCM natürlich gar nicht erst ausgelöst.

Dies ist vermutlich das von Sage gewollte Verhalten und wäre prinzipiell in Ordnung, wenn an irgendeiner anderen Stelle im Sage Quellcode-Ablauf ein "sauberes" und kontextbezogenes Fehler-Management erfolgen würde, so dass Fehler sofort auffallen.

Leider ist dies in der Praxis nicht immer bei allen DCM der Fall!


Beispielsweise ist es ja nach DCM durchaus denkbar, dass die jeweilige Sage Methode, die einen DCM-Call beinhaltet, schon selber einen Exception Handler hat, der daraufhin das Problem lediglich im Tracelog protokolliert, jedoch die Exception nicht wirft. Und das bewirkt dann, dass der eigentliche Sage Quellcode problemlos weiterläuft.

Dies kann dazu führen, dass der Benutzer vor dem Bildschirm der Sage ERP den Fehler nicht einmal sofort auf dem Bildschirm oder beim Drucken usw. bemerkt.

Solche Fälle bedeuten, dass man als Sage-Tech-Partner zunächst seitenlange TraceLog-Aufrufe durcharbeiten muss, um solchen Problemen auf den Grund zu gehen.

⚠️Dies muss man sich bei der Verwendung von DCM entsprechend stets vor Augen führen!⚠️


Problematischer Sage Quellcode zur Abarbeitung der DCM

Die Analyse des Sage internen Codes mit Tools wie "ILSpy" oder "Telerik Just Decompile" hat ergeben, dass die eigentlichen Kernfunktionen für die DCM-Ausführung immer noch nicht genügend Sicherheit bieten, insbesondere wenn mehrere Aktionen für dieselbe DCM registriert sind (=Einsatz mehrer Anpassungen von verschiedenen Techpartnern bei einem Endkunden, die auf dieselbe DCM reagieren).


Entscheidender "Startpunkt" für die DCM-Ausführung ist Sage intern die private Methode "Sagede.OfficeLine.Engine.Mandant.ExecuteDCM" am Mandantenobjekt.

Die eigentliche DCM-Ausführung erfolgt in folgendem privaten Methoden-Context: "Sagede.OfficeLine.Shared.Customizing.DcmDispatcher.ExecuteGeneral".

Die Analyse dieses Quellcodes zeigt u.a. dass die gesamte DCM Abarbeitung sofort verlassen wird, sobald bei irgendeinem DCM-Registrierungspunkt (=Aufruf eines registrierten DLL-Einstiegspunkts) irgendeine Exception auftreten sollte.


D.h. Sage unterstellt, dass irgendeine Exception oder ein Problem bei irgendeiner der unter Umständen x registrierten DLL-Behandlungen (z.B. bei mehreren Anpassungen verschiedener Sage Techpartner) die gesamte DCM-Weiterbearbeitung nachfolgender DLL´s zwingend abgebrochen werden muss.


⚠️ Es existiert also immer noch kein zuverlässiges Error-Management in der DCM-Abarbeitung seitens Sage und keine "Feinsteuerungsmöglichkeit", um zu erreichen, dass nachfolgender wichtiger DCM Tech Partner Quellcode trotzdem ausgeführt wird.⚠️


Sage hat einige TraceLog-Aufrufe hinzugefügt, so dass man das Problem im Idealfall schneller identifizieren kann; nach diesen Einträgen sollte man bei Problemen auf jedem Fall im Sage TraceLog schauen, Beispiel:

logger.Error(string.Concat("DCM - DcmDispatcher.ExecuteGeneral threw exception", exception.Message));

Beispiel für Problemsituation

Wenn z.B. zwei verschiedene Assemblies für diesselbe DCM registriert wurden, jedoch bei der ersten .net-Assembly tritt im Sage Quellcode zur Abarbeitung der DCM eine Exception auf, dann wird die gesamte DCM-Abarbeitung für den DCM-Call von Sage vorzeitig abgebrochen.

Nachfolgende DCM werden dann nicht mehr ausgeführt.


Hat ein Endkunde z.B. irgendein "problematisches" DCM-AddOn/Zusatzprogrammierung von einem Sage Techpartner X im Einsatz, so ist es sehr gut möglich, dass der Quellcode von Sage Techpartner Y, der auf dieselbe DCM reagiert, dann einfach nicht mehr ausgeführt wird!


Nehmen wir für einen Moment aber einfach mal an, dass aus Sicht des Endkunden die Anpassung von Techpartner Y sehr viel wichtiger und "kritisch" für die Abläufe des Endkunden sei.

Sagen wir mal, es geht um eine DCM im Lagerbereich und die Anpassung von Techpartner Y soll sehr wichtige automatische Lager-Folgebuchungen ausführen, während vielleicht die vorher gelaufende DCM-Abarbeitung von Techpartner X lediglich die Lagerbuchung für eine Statistik auswertet.

Es liegt auf der Hand, dass Fehler in der DCM-Abarbeitung von Techpartner X auf jeden Fall protokolliert gehören, jedoch sollte die Techpartner Y-Anpassung in jedem Fall trotzdem ausgeführt werden.


  • Im günstigsten Fall fällt das Problem zumindest auf, wenn Sage Exceptions bis an die jeweilige Programmfunktion in das Frontend weitergibt, sichtbar für die Anwender.

  • Im ungünstigsten Fall handelt es sich um eine DCM, die nicht so vorgeht und niemand bekommt etwas unmittelbar von dem Problem mit. ⚠️Vielleicht werden dann erstmal hunderte von Lagerbuchungen falsch erzeugt (weil die genannten Folgebuchungen fehlen) und das Ganze fällt erst sehr viel später auf, bei der Inventur z.B.⚠️


Aktuell bekannte Workarounds


Sage Assembly direkt ändern


Aktuell besteht der zuverlässigste Workaround darin, in sehr "kritischen" Anpassungen (für den Kunden wichtig , daher "kritisch" für den Gesamtablauf) den eigenen Code direkt als Aufruf in die jeweilige betroffene Sage Assembly zu integrieren und diese somit direkt zu ändern (z.B. "BelegEngine" oder "PrintEngine").

D.h. die eigene Assembly direkt der Sage Assembly als Verweis hinzufügen und dann die Klasse/Methode des jeweiligen eigenen Codes direkt an der passenden Stelle in der Sage Assembly einbauen.


Diesen Ansatz haben wir bei Kunden mit "kritischen" Projekten gewählt und die gekapselten eigenen Quellcode unverändert statt über die DCM direkt aus der angepassten Sage Assembly ausgelöst. 

Danach gab es bei diesen Kunden keinerlei Probleme mit den DCM mehr, was eindeutig zeigte, dass nicht unser eigentlicher Quellcode in der DCM das Problem war, sondern stattdessen dass Sage gar nicht erst den DCM-Quellcode von uns ausgelöst hatte (vielleicht weil ein vorher ausgeführter DCM-Quellcode eines anderen Techpartners auf einen Fehler lief!).


⚠️Die Sage Assemblies können ausschließlich von Sage Techpartnern geändert werden, die über den entsprechenden Sage Quellcode verfügen.

Ein weiterer Nachteil ist, dass der Endkunde dadurch nicht mehr ohne Anpassung der Assemblies ein Sage LiveUpdate durchführen kann, bevor der Techpartner die Anpassungen für das neue LiveUpdate bereitstellt.

Außerdem ist für die Anpassung von Sage Assemblies (und MS-Access-AddIns oder des Frontends) zwingend die Lizenzoption "Erweiterte Clientanpassbarkeit" notwendig, die u.U. nicht jeder Kunde nutzen kann.⚠️



AppDesigner Makros als Alternative!?


Je nach Situation, z.B. beim Druck, kann der .net-Code für eine eigene Druckaufbereitung auch durch ein passendes AppDesigner Makro ausgelöst werden.

Versuche bei einem Kunden haben gezeigt, dass dies möglich ist (wenn auch umständlich und mehr Entwicklungsaufwand).

Es hängt entscheidend von der Anforderung/vom Projekt ab, ob überhaupt ein passendes AppDesigner Makro zur Verfügung steht.



English


Situation

Defined Sage DLL-Common-Methods (DCM) are not always triggered at runtime or run into errors that are not noticeable immediately.


Frequency of problems

The problem basically affects all Sage 100 versions!

At one of our customers, we used a control function to "measure" how often, for example, a print DCM from Sage was not reliably triggered or ran through completely.


With the Sage 100 Version 8.0, it turned out that a print DCM was not triggered for about every 20th print.

From the Sage 100 version 8.1 out of 161 prints, the DCM was not triggered 9 times, so the problem persists.

From the Sage 100 version 9.0 onwards, we have recently noticed the same problem with some installations.

We are using numerous DCM in connection with sales documents for a larger customer (larger installation, around 50 users).

There was also a log if DCM calls failed (because important objects were not set).

Our log showed that over the course of a year, several dozen times, the DCM was not triggered or was aborted due to errors in Sage's internal code.

See the following example source code for a reaction to a DCM: DCM Source code .


Probable cause of the problem

We suspect Sage's approach to processing the DCM favours the problems, especially if several DCM are registered with a customer, e.g. from different Sage Tech partners.

It´s easily possible any errors in the DCM processing chain may result in the entire registered DCM source code of all other Tech Partners not being processed correctly.

The situation is likely to be particularly problematic for end customers who use several adaptations from various Sage Tech partners who also react to the same DCM with .net source code (see example below).


Another important reason is that the Sage code, which triggers the actual DCM internally inside the Sage objects, can run into some error, i.e. the Sage source code does not even get to the point at which the DCM is triggered at runtime.


Internally, the main "starting point" for the DCM execution is the private method "Sagede.OfficeLine.Engine.Mandant.ExecuteDCM" in the Sage mandator object, "Mandant".

This means that various Sage Assemblies ultimately call this method of the "Mandant" object at appropriate points in the source code in order to trigger DCM execution.

Below is a code example from the disassembled original Sage source code of the private method "Sagede.OfficeLine.Wawi.PrintEngine.Belegdruck.InitializeTempTables".


See Sage 100 Source code.


At the end of this source code you can see the DCM call "Me.DCMCallBeleg(DcmListId.PrintEKInitTables, DcmListId.PrintVKInitTables)".

If any exception is thrown before this call in this method (or in the one before it), the call of the DCM is of course not even triggered.

This is probably the behaviour intended by Sage and would, in principle, be okay if "clean" and context-related error management were to take place at some other point in the Sage source code flow so that errors are immediately noticeable.

Unfortunately, this is not always the case with all DCM in practice!


For example, it is quite conceivable that the respective Sage method, which contains a DCM call, already has an exception handler, which then only logs the exception in the trace log but does not throw the exception. This then leads to the situation that the actual Sage source code continues to run without any problems.

This might result in a situation where the user in front of the screen of the Sage ERP does not even notice an error immediately on the screen or when printing, etc.

Such cases mean that as a Sage Tech Partner, you first have to work your way through pages of TraceLog calls to get to the bottom of such problems.

⚠️This must always be kept in mind when using DCM!⚠️


Problematic Sage source code for processing the DCM

Analysis of Sage's internal code with tools such as "ILSpy" or "Telerik Just Decompile" has shown that the actual core functions for DCM execution still do not offer sufficient security, especially when multiple actions are registered for the same DCM (=use of multiple addons/additional programmings from different tech partners at an end customer reacting to the same DCM).


Internally, the main "starting point" for the DCM execution is the private method "Sagede.OfficeLine.Engine.Mandant.ExecuteDCM" in the "Mandant" object.

The actual DCM execution takes place in the following private method context: "Sagede.OfficeLine.Shared.Customizing.DcmDispatcher.ExecuteGeneral".

The analysis of this source code shows, among other things, that the entire DCM processing is exited immediately as soon as any exception should occur at any DCM registration point (=call of a registered DLL entry point).


I.e. Sage assumes that for any exception or problem with any of the possibly x registered DLL treatments (e.g. with several adjustments by different Sage tech partners), the entire DCM further processing of subsequent DLLs must be aborted.


⚠️ So there is still no reliable error management in the DCM processing on the part of Sage and no "fine tuning" option to ensure that any important subsequent DCM tech partner code for the same DCM is still being triggered.⚠️


Sage added some TraceLog calls, so ideally you can identify the problem faster; You should always look for these entries in the Sage TraceLog in the event of problems, example:

logger.Error(string.Concat("DCM - DcmDispatcher.ExecuteGeneral threw exception", exception.Message));

Example for a problematic situation

If, for example, two different assemblies were registered for the same DCM, but an exception occurs in the Sage source code for processing the DCM in the first .net assembly, then the entire DCM processing for the DCM call from Sage is aborted prematurely.

Subsequent DCMs are then no longer executed.


If an end customer is using any "problematic" DCM addon/additional programming from a Sage Tech Partner X, it is very well possible that the source code from Sage Tech Partner Y, which reacts to the same DCM, is then simply no longer executed!


Now, let´s assume for a moment, from the customer's point of view, the adaptation of Tech Partner Y may be much more important and "critical" for the end customer´s processes.


Let´s imagine the whole situation is about a DCM in the stock booking area, and the adjustment of Tech Partner Y should carry out very important automatic stock follow-up bookings, while perhaps the previously run DCM source code of Tech Partner X only evaluates the stock booking for a statistic.

It is obvious that errors in Tech Partner X's DCM processing should definitely be logged, but Tech Partner Y's adjustment should be executed anyway.


  • In the best case scenario, the problem is at least noticeable by the users when Sage passes exceptions on to the respective program function in the front end.

  • In the worst case, it is a DCM that does not do this, and no one is immediately aware of the problem. ⚠️Perhaps hundreds of stock bookings are then generated incorrectly (because the above mentioned important follow-up bookings are missing), and all this only becomes apparent much later, during stock-taking/inventory, e.g.⚠️


Currently known workarounds


Change the Sage assembly directly


Currently, the most reliable workaround is to integrate your own code directly as a call into the respective affected Sage assembly for very "critical" adjustments (important for the customer, therefore "critical" for the overall process) and thus change the assembly directly (e.g. "BelegEngine" or "PrintEngine").

I.e., adding your own assembly directly to the Sage assembly as a reference and then putting the class/method call of your own code directly at the appropriate place within the Sage assembly.


We chose this approach for customers with "critical" projects and triggered the encapsulated own unchanged source code instead of via the DCM directly from the Sage assembly.

After that, these customers had no more problems with the DCM, which clearly showed that our actual source code in the DCM was never the cause of the problem but it was rather because Sage did simply not trigger our DCM source code in the first place (maybe due to an error in another Tech Partner´s previously triggered DCM source code).


⚠️Sage assemblies can only be modified by Sage Tech Partners who have the Sage source code available.

Another disadvantage is that the end customer can no longer carry out a Sage LiveUpdate without having the Sage assemblies adapted by the Tech Partner for the new LiveUpdate.

In addition, for changing Sage assemblies (or MS-Access-AddIns or the frontend) the "Erweiterte Clientanpassbarkeit" license option is required, which may not be available to every customer.⚠️



AppDesigner macros as an alternative!?


Depending on the situation, e.g. when printing, the .net code for your own print processing can also be triggered by a suitable AppDesigner macro.

Trials with a customer have shown that this is possible (albeit cumbersome and it requires more development work).

It depends on the individual requirement/project if a suitable AppDesigner macro is available at all.



Sage 100 Source code

Private Sub InitializeTempTables()
 
  Dim clause As String
 
  If Me.IstPrepareInfoDruck Then
    'für den SQL-Server-Vergleich muss ein neutral-konvertiertes Datum verwendet werden, ohn Berücksichtigung von Konvertierung und Ländereinstellungen
    clause = "InfodruckId > 0 AND InfodruckDatum < " & DateTimeHelper.DateToAdo(Me.InfodruckDatumDelete)
  Else
    clause = "ConnID = " + Convert.ToString(Me.ConnectionID)
  End If
 
  Me.Mandant.MainDevice.ConnectionAdoNet.ExecuteNonQuery("DELETE FROM tKHKPrintMain WHERE " + clause)
  Me.Mandant.MainDevice.ConnectionAdoNet.ExecuteNonQuery("DELETE FROM tKHKPrintPositionText WHERE " + clause)
  Me.Mandant.MainDevice.ConnectionAdoNet.ExecuteNonQuery("DELETE FROM 

  ...
 
  'DCM-Aufruf
  'Immer am Ende
  Me.DCMCallBeleg(DcmListId.PrintEKInitTables, DcmListId.PrintVKInitTables)
 
End Sub

DCM Source code

public bool Entry(Sagede.OfficeLine.Shared.Customizing.IDcmContext context)
{
  if (context.ListId == DcmDefinitionManager.DcmListId.PrintVKInitTables)
  {
    #region "Special print log"
   
    if (ErpPrintLog.GetPrintLogIsActive(this._mandant))
    {
      // Own special logging to document if the DCM is fired
      ErpPrintLog.WriteLogWritePrintMessage(this._mandant, "20160821100: Dcm 'DcmDefinitionManager.DcmListId.PrintVKInitTables' was triggered.", MessageType.Information, "20160821100",  methodId);
    }
   
    #endregion
   
    // Cast Sage common interface in actual DCM context
    var printContext = this.GetEntryForPrint(context);
   
    // Trigger real action in separate class (makes it easy to call this source also directly from any Sage Assembly, in case of DCM problems!)
    if (printContext != null
         && this._printHelper != null)
    {
      this._printHelper.ClearTempPrintTable(this._printParameters);
    }
  }
}


bottom of page