Ist Transformation von Xbase++ in JAVA möglich?

Alle Fragen um die Programmierung, die sich sonst nicht kategorisieren lassen. Von Makro bis Codeblock, von IF bis ENDIF

Moderator: Moderatoren

Antworten
Werner
Rekursionen-Architekt
Rekursionen-Architekt
Beiträge: 124
Registriert: Sa, 18. Mär 2006 16:08

Ist Transformation von Xbase++ in JAVA möglich?

Beitrag von Werner »

Hi!

Heute hätte ich einmal ein ganz verrückte Frage: Kann man ein XBase++ Programm (auf Basis der Version 1.82) in eine andere Programmiersprache
(z.B. Java) transferieren und wenn ja, wer kann so etwas?

Gruß Werner
Benutzeravatar
Tom
Der Entwickler von "Deep Thought"
Der Entwickler von "Deep Thought"
Beiträge: 9447
Registriert: Do, 22. Sep 2005 23:11
Wohnort: Berlin
Hat sich bedankt: 111 Mal
Danksagung erhalten: 374 Mal
Kontaktdaten:

Re: Ist Transformation von Xbase++ in JAVA möglich?

Beitrag von Tom »

Hallo, Werner.

Es gibt syntaktische und inhaltliche Unterschiede zwischen Xbase++ und Java, es gibt Funktionen, Bibliotheken und Klassen, die Xbase++-Anwendungen benötigen, die aber in Java nicht oder ganz anders vorhanden sind. Der Aufbau der Programme ist unterschiedlich.

Vermutlich lässt sich ein Programm, das in Xbase++ geschrieben wurde, auch in Java erstellen (und umgekehrt), aber einen Transpiler von hier nach dort gibt es nicht.
Herzlich,
Tom
Benutzeravatar
mikehoffmann
Rekursionen-Architekt
Rekursionen-Architekt
Beiträge: 139
Registriert: Mo, 21. Sep 2015 16:22
Hat sich bedankt: 1 Mal
Danksagung erhalten: 23 Mal

Re: Ist Transformation von Xbase++ in JAVA möglich?

Beitrag von mikehoffmann »

Hallo Werner,

ich habe einen Transpiler nach C++ geschrieben. Breeze heißt mein kleiner Freund. Leider ist das gute Stück noch nicht ganz fertig, da ich bisher nicht dazu kam, die C++ Laufzeitbibliotheken zu schreiben. Zeit ist der begrenzende Faktor. Das merke ich schon am Alter. Aber wenn es nötig werden sollte, kann ich zumindest die Schublade aufmachen und ans Werk gehen.

Einmal durchprogrammiert könnte man richtig fett die Vorteile ernten, die uns bei Xbase++ versagt sind. Typen zum Beispiel, die das Programm um den Faktor 1000 oder mehr schneller machen könnten. 64 Bit große Adressen sind einen Compiler-Schalter entfernt. Die C++ Code-Optimierung bekäme man geschenkt. Und die haut richtig rein. Ach ja, und bloß nicht vergessen: Platformunabhängigkeit. Wie wäre es mal mit einem Xbase++ (oder besser Breeze++) Programm auf einem ESP32? Die Dinger gehen ab wie Zäpfchen.

Die Code-Erzeugung nach Java zu ändern wäre kein Problem, die Auswahl einer Zielsprache ist vorgesehen. Xbase++ kann man schon wählen, damit kann ich Sprachergänzungen implementieren.

Nehmen wir mal an, die Java Codeerzeugung würde stehen. Das wäre kein Hexenwerk. Kitzelig wird es dann, wenn Du mit Java Xbase Parts arbeiten wolltest, denn deren Implementierung wäre auch Handbetrieb, diesmal von der unangenehmen Sorte. Dasselbe gilt für die in die Sprache integrierte Datenbankfunktionalität. dbf/ntx/cdx und Kollegen. Aber alles machbar.

Viele Grüße
Michael
Benutzeravatar
Werner_Bayern
Der Entwickler von "Deep Thought"
Der Entwickler von "Deep Thought"
Beiträge: 2150
Registriert: Sa, 30. Jan 2010 22:58
Wohnort: Niederbayern
Hat sich bedankt: 31 Mal
Danksagung erhalten: 78 Mal

Re: Ist Transformation von Xbase++ in JAVA möglich?

Beitrag von Werner_Bayern »

Es gibt ähnliches: https://www.xsharp.eu/articles/x-summit-2024
The event will have 3 different tracks:
• Conversion of apps from Visual Objects and Vulcan to .Net using X#
• Conversion of apps from Visual FoxPro to .Net using X#
• General topics about X# and .Net development
es grüßt

Werner

<when the music is over, turn off the lights!>
ramses
Der Entwickler von "Deep Thought"
Der Entwickler von "Deep Thought"
Beiträge: 2526
Registriert: Mi, 28. Jul 2010 17:16
Hat sich bedankt: 12 Mal
Danksagung erhalten: 79 Mal

Re: Ist Transformation von Xbase++ in JAVA möglich?

Beitrag von ramses »

@MikeHoffmann

Hallo Michael

da kommen doch sofort die alten Wünsche nach mehr Speicher, 64Bit und Multicore CPU Nutzung wieder auf....

Wäre es mit deinem Transpiler auch möglich auf Rust zu übersetzen?
Valar Morghulis

Gruss Carlo
Benutzeravatar
mikehoffmann
Rekursionen-Architekt
Rekursionen-Architekt
Beiträge: 139
Registriert: Mo, 21. Sep 2015 16:22
Hat sich bedankt: 1 Mal
Danksagung erhalten: 23 Mal

Re: Ist Transformation von Xbase++ in JAVA möglich?

Beitrag von mikehoffmann »

Hallo Carlo,

überhaupt kein Problem. Der Clou ist ja, dass es nach der Code-Analyse einen CodePart-Baum (CLASS CodePart) gibt, der für die Codeerzeugung durchtraversiert wird.

Für jede Zielsprache gibt es in jedem CodePart eine eigene Codeerzeugungsmethode. Derzeit gibt es die Methoden :XppWriteCode() für Xbase++ Zielcode und :CppWriteCode() für C++ Code.

Dieser Baum hat als Root-Element das ProgramModule (das entspricht dem .prg), das aus seinen Deklarationen und lauter CodeParts besteht. Das sind auf oberster Ebene FuncProcMets. Die bestehen dann wieder aus weiteren CodeParts. Nachfolgend poste ich beispielhaft die Codeparts für die Kontroll-Konstrukte. Der Analyzer erzeugt die Objekte mit den :Init-Methoden (und füllt deren Instanzvariablen im weiteren Verlauf) und wenn das Programm erfolgreich durchanalysiert wurde, dann läuft die Codeerzeugung los. Die besteht dann nur noch aus lauter :XxxWriteCode() -Methodenaufrufen. Wären in Deinem Fall die :RustWriteCode()-Methoden.

Viele Grüße
Michael

Code: Alles auswählen

SECTION ControlConstructs

   SECTION ControlConstruct

      CLASS ControlConstruct FROM CodePart
       EXPORTED:
         CLASS METHOD InitClass
         METHOD Init
         METHOD AutoClose
      ENDCLASS

      CLASS METHOD ControlConstruct:InitClass
      RETURN ::CodePart:InitClass()

      METHOD ControlConstruct:Init
      RETURN ::CodePart:Init()

      METHOD ControlConstruct:AutoClose
         ErrorMessage("Program structure flawed.")
      RETURN .F.

   ENDSECTION

   SECTION IfConstruct

      CLASS IfConstruct FROM ControlConstruct
       EXPORTED:
         VAR branches    && Array { { <if-expression> | NIL , { [<code>,...] } } , ... }
         VAR elseLineNo
         CLASS METHOD InitClass
         METHOD Init
         METHOD AddElseIfBranch
         METHOD AddElseBranch
         METHOD AddCode
         METHOD XppWriteCode
         METHOD CppWriteCode
      ENDCLASS

      CLASS METHOD IfConstruct:InitClass
      RETURN ::ControlConstruct:InitClass()


      METHOD IfConstruct:Init(expression)
        ::ControlConstruct:Init()
        ::branches := { {expression, {} }}
      RETURN self


      METHOD IfConstruct:AddElseIfBranch(expression)

        * There was already an ELSE Branch
        IF ATail(::branches)[1] == NIL
           ErrorMessage("Illegal ELSEIF after ELSE branch.")
           RETURN .F.
        ENDIF

        * Open a new ELESIF branch
        AAdd(::branches, {expression, {} })

      RETURN self


      METHOD IfConstruct:AddElseBranch

        * There was already an ELSE Branch
        IF ATail(::branches)[1] == NIL
           ErrorMessage("Illegal second ELSE branch.")
           RETURN .F.
        ENDIF

        * Take down the line number of the ELSE statement
        ::elseLineNo := GetActualPrgLineNo() && sourcePosition:sourceLine:lineNo

        * Open an ELSE branch
        AAdd(::branches, { NIL, {} })

      RETURN self

      METHOD IfConstruct:AddCode(code)
         AAdd(ATail(::branches)[2],code)
      RETURN self

      SECTION XPP Code Creation

         METHOD IfConstruct:XppWriteCode

            LOCAL n

            WriteOutputLine("IF " + ::branches[1][1]:GetCode(),::GetSourceLineNo())

            MoreIndent()

            AEval(::branches[1][2],{|elem|elem:XppWriteCode()})

            FOR n := 2 TO Len(::branches)

                SlightlyLessIndent()

                * It's the ELSE branch?
                IF ::branches[n][1] == NIL
                   WriteOutputLine("ELSE",::elseLineNo)
                  ELSE
                   WriteOutputLine("ELSEIF "+::branches[n][1]:GetCode(),::branches[n][1]:GetSourceLineNo())
                ENDIF
                SlightlyMoreIndent()

                AEval(::branches[n][2],{|elem|elem:XppWriteCode()})

            NEXT n

            LessIndent()
            WriteOutputLine("ENDIF",::GetClosingLine())

         RETURN self

      ENDSECTION

      SECTION CPP Code Creation

         METHOD IfConstruct:CppWriteCode

            LOCAL n

            CPPSetSourceLineNumber(::branches[1][1])

            WriteOutputLine("// IF "+::branches[1][1]:GetCode())
            WriteOutputLine("if ( FASTBOOL("+::branches[1][1]:CPPGetCode()+") ) {")
            WriteOutputLine("")
            MoreIndent()

            AEval(::branches[1][2],{|elem|elem:CPPWriteCode()})
            WriteOutputLine("}")

            FOR n := 2 TO Len(::branches)

                SlightlyLessIndent()

                * It's the ELSE branch?
                IF ::branches[n][1] == NIL
                   WriteOutputLine("// ELSE")
                   WriteOutputLine("else {")
                  ELSE

                   CPPSetSourceLineNumber(::branches[n][1])

                   WriteOutputLine("// ELSEIF "+::branches[n][1]:GetCode())
                   WriteOutputLine("else if ( FASTBOOL("+::branches[n][1]:CPPGetCode()+") ) {")

                ENDIF
                WriteOutputLine("")
                SlightlyMoreIndent()

                AEval(::branches[n][2],{|elem|elem:CPPWriteCode()})
                WriteOutputLine("}")

            NEXT n
            WriteOutputLine("")

            LessIndent()
            WriteOutputLine("// ENDIF")
            WriteOutputLine("")
         RETURN self

      ENDSECTION

   ENDSECTION

   SECTION CaseConstruct

      CLASS CaseConstruct FROM ControlConstruct
       EXPORTED:
         VAR branches    && Array { { <case-expression> | NIL , { [<code>,...] } } , ... }
         VAR otherwiseLineNo
         CLASS METHOD InitClass
         METHOD Init
         METHOD AddCaseBranch
         METHOD AddOtherwiseBranch
         METHOD AddCode
         METHOD XppWriteCode
         METHOD CppWriteCode
      ENDCLASS

      CLASS METHOD CaseConstruct:InitClass
      RETURN ::ControlConstruct:InitClass()

      METHOD CaseConstruct:Init()
        ::ControlConstruct:Init()
        ::branches := { }
      RETURN self

      METHOD CaseConstruct:AddCaseBranch(expression)

        * There was already an ELSE Branch
        IF .NOT. Empty(::branches) .AND. (ATail(::branches)[1] == NIL)
           ErrorMessage("Illegal CASE after OTHERWISE branch.")
           RETURN .F.
        ENDIF

        * Open a new CASE branch
        AAdd(::branches, {expression, {} })

      RETURN self

      METHOD CaseConstruct:AddOtherwiseBranch

        * There was already an OTHERWISE Branch
        IF (Len(::branches) > 0) .AND. (ATail(::branches)[1] == NIL)
           ErrorMessage("Illegal second OTHERWISE branch.")
           RETURN .F.
        ENDIF

        * Take down the line number of the ELSE statement
        ::otherwiseLineNo := GetActualPrgLineNo() && sourcePosition:sourceLine:lineNo

        * Open an OTHERWISE branch
        AAdd(::branches, { NIL, {} })

      RETURN self

      METHOD CaseConstruct:AddCode(code)
         IF Empty(::branches)
            ErrorMessage("No executable code allowed before first CASE statement.")
           ELSE
            AAdd(ATail(::branches)[2],code)
         ENDIF
      RETURN self

      SECTION Xpp Code Creation

         METHOD CaseConstruct:XppWriteCode

            LOCAL n

            WriteOutputLine("DO CASE",::GetSourceLineNo())

            MoreIndent()

            FOR n := 1 TO Len(::branches)

                * It's the OTHERWISE branch?
                IF ::branches[n][1] == NIL
                   WriteOutputLine("OTHERWISE",::otherwiseLineNo)
                  ELSE
                   WriteOutputLine("CASE "+::branches[n][1]:GetCode(),::branches[n][1]:GetSourceLineNo())
                ENDIF

                MoreIndent()
                AEval(::branches[n][2],{|elem|elem:XppWriteCode()})
                LessIndent()

            NEXT n

            LessIndent()

            WriteOutputLine("ENDCASE",::GetClosingLine())

         RETURN self

      ENDSECTION

      SECTION CPP Code Creation

         METHOD CaseConstruct:CppWriteCode

            LOCAL n
            LOCAL elseIfBracketCounter := 0


            * Special case: DO CASE / OTHERWISE / ENDCASE
            IF (Len(::branches) == 1) .AND. (::branches[1][1] == NIL)

                WriteOutputLine("// DO CASE OTHERWISE only")
                WriteOutputLine("")

                MoreIndent()
                AEval(::branches[1][2],{|elem|elem:CPPWriteCode()})
                WriteOutputLine("")
                LessIndent()
                WriteOutputLine("// ENDCASE")
                WriteOutputLine("")
                RETURN self

            ENDIF

            WriteOutputLine("// DO CASE")
            WriteOutputLine("")

            MoreIndent()

            FOR n := 1 TO Len(::branches)

                * It's the OTHERWISE branch?
                IF ::branches[n][1] == NIL
                   WriteOutputLine("// OTHERWISE")
                   WriteOutputLine("")
                   elseIfBracketCounter--
                  ELSE
                   CPPSetSourceLineNumber(::branches[n][1])
                   WriteOutputLine("// CASE " + ::branches[n][1]:CPPGetCode())
                   WriteOutputLine("if ( FASTBOOL("+::branches[n][1]:CPPGetCode()+") ) {")
                   WriteOutputLine("")
                ENDIF

                MoreIndent()
                AEval(::branches[n][2],{|elem|elem:CPPWriteCode()})
                IF n < Len(::branches)
                   WriteOutputLine("} else {")
                   elseIfBracketCounter++
                  ELSE
                   WriteOutputLine("}")
                ENDIF

                WriteOutputLine("")
                LessIndent()

            NEXT n

            WriteOutputLine(Replicate("}",elseIfBracketCounter)+";")

            LessIndent()
            WriteOutputLine("// ENDCASE")
            WriteOutputLine("")

         RETURN self

      ENDSECTION

   ENDSECTION

   SECTION WhileConstruct

      CLASS WhileConstruct FROM ControlConstruct
       EXPORTED:
         VAR condition
         VAR loopCode
         CLASS METHOD InitClass
         METHOD Init
         METHOD AddCode
         METHOD XppWriteCode
         METHOD CppWriteCode
      ENDCLASS

      CLASS METHOD WhileConstruct:InitClass
      RETURN ::ControlConstruct:InitClass()

      METHOD WhileConstruct:Init(expression)
        ::ControlConstruct:Init()
        ::condition := expression
        ::loopCode := { }
      RETURN self

      METHOD WhileConstruct:AddCode(code)
         AAdd(::loopCode,code)
      RETURN self


      SECTION XPP Code Creation

         METHOD WhileConstruct:XppWriteCode
            WriteOutputLine("WHILE "+::condition:GetCode(),::GetSourceLineNo())
            MoreIndent()
            AEval(::loopCode,{|elem|elem:XppWriteCode()})
            LessIndent()
            WriteOutputLine("ENDDO",::GetClosingLine())
         RETURN self

      ENDSECTION

      SECTION CPP Code Creation

         METHOD WhileConstruct:CppWriteCode

            CPPSetSourceLineNumber(::condition)
            WriteOutputLine("// DO WHILE "+::condition:GetCode())
            WriteOutputLine("while ( FASTBOOL("+::condition:CPPGetCode()+") ) {")
            WriteOutputLine("")
            MoreIndent()
            AEval(::loopCode,{|elem|elem:CPPWriteCode()})
            CppSetSourceLineNumber(::condition)
            LessIndent()
            WriteOutputLine("} // ENDDO")
            WriteOutputLine("")
         RETURN self

      ENDSECTION

   ENDSECTION

   SECTION SequenceConstruct

      CLASS SequenceConstruct FROM ControlConstruct
       EXPORTED:
         VAR normalCode
         VAR recoverCode
         VAR recoverVar
         VAR recoverLineNo
         CLASS METHOD InitClass
         METHOD Init
         METHOD AddCode
         METHOD InProtectedCode
         METHOD BeginRecoverBranch
         METHOD XppWriteCode
      ENDCLASS

      CLASS METHOD SequenceConstruct:InitClass
      RETURN ::ControlConstruct:InitClass()

      METHOD SequenceConstruct:Init()
        ::ControlConstruct:Init()
        ::normalCode := {}
      RETURN self

      METHOD SequenceConstruct:AddCode(code)

        * Recover part already started?
        IF ::recoverCode # NIL
           AAdd(::recoverCode,code)
          ELSE
           AAdd(::normalCode,code)
        ENDIF

      RETURN self


      METHOD SequenceConstruct:InProtectedCode
        * We're in protected code as long as we're not yet in recover branch
      RETURN ::recoverCode == NIL


      METHOD SequenceConstruct:BeginRecoverBranch(varName)

         IF ::recoverCode # NIL
            ErrorMessage("Illegal second RECOVER branch.")
           ELSE

            * Initialize the recover stuff
            ::recoverCode := {}
            ::recoverVar := varName

            * Take down the line number of the RECOVER statement
            ::recoverLineNo := GetActualPrgLineNo() && sourcePosition:sourceLine:lineNo

         ENDIF

      RETURN self


      SECTION XPP Code Creation

         METHOD SequenceConstruct:XppWriteCode

            WriteOutputLine("BEGIN SEQUENCE",::GetSourceLineNo())

            MoreIndent()

            AEval(::normalCode,{|elem|elem:XppWriteCode()})

            IF ::recoverCode # NIL
               SlightlyLessIndent()
               WriteOutputLine("RECOVER " + IIf(::recoverVar#NIL,"USING "+::recoverVar,""),::recoverLineNo)
               SlightlyMoreIndent()
               AEval(::recoverCode,{|elem|elem:XppWriteCode()})
               LessIndent()
            ENDIF

            WriteOutputLine("END SEQUENCE",::GetClosingLine())

         RETURN self

      ENDSECTION

      SECTION CPP Code Creation

           * To be written

      ENDSECTION

   ENDSECTION

   SECTION ForNextConstruct

      CLASS ForNextConstruct FROM ControlConstruct
       EXPORTED:
         VAR loopVarExpression
         VAR fromExpression
         VAR toExpression
         VAR stepExpression
         VAR loopcode
         VAR loopNumber    && Number of for-next-loop in funprocmet, for loop var bag house keeping
         CLASS METHOD InitClass
         METHOD Init
         METHOD AddCode
         METHOD XppWriteCode
         METHOD CppWriteCode
      ENDCLASS

      CLASS METHOD ForNextConstruct:InitClass
      RETURN ::ControlConstruct:InitClass()

      METHOD ForNextConstruct:Init(loopVarExpression,fromExpression,toExpression,stepExpression)
        ::ControlConstruct:Init()
        ::loopVarExpression := loopVarExpression
        ::fromExpression := fromExpression
        ::toExpression := toExpression
        ::stepExpression := stepExpression
        ::loopCode := {}
        ::loopNumber := ++currentFuncProcMet:forNextLoopCount
      RETURN self

      METHOD ForNextConstruct:AddCode(code)
         AAdd(::loopCode,code)
      RETURN self


      SECTION XPP Code Creation

         METHOD ForNextConstruct:XppWriteCode
            WriteOutputLine("FOR "+::loopVarExpression:GetCode()+" := "+::fromExpression:GetCode()+" TO "+::toExpression:GetCode();
                            +IIf(::stepExpression#NIL," STEP "+::stepExpression:GetCode(),""),::GetSourceLineNo())
            MoreIndent()
            AEval(::loopCode,{|elem|elem:XppWriteCode()})
            LessIndent()
            WriteOutputLine("NEXT",::GetClosingLine())
         RETURN self

      ENDSECTION

      SECTION CPP Code Creation

         METHOD ForNextConstruct:CppWriteCode

            LOCAL loopBagName := SVI("forNextBag[&1]",::loopNumber-1)

            CPPSetSourceLineNumber(self)

            WriteOutputLine("//" + ::ControlConstruct:SourceLink:statement:compSource)
            WriteOutputLine(loopBagName+" = "+::loopVarExpression:CppGetCode()+";")
            WriteOutputLine(loopBagName+"->Assign("+::fromExpression:CppGetCode()+");")
            WriteOutputLine("while ( ForNextCheck("+loopBagName+",";
                                                    +::toExpression:CPPGetCode()+",";
                                                    +IIf(Empty(::stepExpression),"StockOne()",::stepExpression:CPPGetCode());
                                                    + ") ) {")

            MoreIndent()
            AEval(::loopCode,{|elem|elem:CPPWriteCode()})

            CPPSetSourceLineNumber(self)

            IF Empty(::stepExpression)
               WriteOutputLine(loopBagName + "->UnOpPreIncrement();")
              ELSE
               WriteOutputLine(loopBagName + "->AddAssign(" + ::stepExpression:CPPGetCode() + ");")
            ENDIF


            LessIndent()
            WriteOutputLine("} // NEXT") && + ::loopVarExpression:CppGetCode())
            WriteOutputLine("")
         RETURN self

      ENDSECTION

   ENDSECTION

ENDSECTION
ramses
Der Entwickler von "Deep Thought"
Der Entwickler von "Deep Thought"
Beiträge: 2526
Registriert: Mi, 28. Jul 2010 17:16
Hat sich bedankt: 12 Mal
Danksagung erhalten: 79 Mal

Re: Ist Transformation von Xbase++ in JAVA möglich?

Beitrag von ramses »

Hallo Michael

vielleicht hat du ja noch eine Idee und ich muss ich ja nicht unbedingt zu RUST wechseln.

Mein Problem ist die fehlende Multi-Core Unterstützung für Threads, die 32Bit Grenze von Xbase++ und oft auch die Speicherverwaltung. Die App ist mittlerweile eine komplette Web-App mit xb2net die List&Label und direkt Postgresql 16.3 (keine DBE) verwendet und als Service oder Consolen-App gestartet wird. Als Abhilfe für die fehlende Performance haben wir die Aufgaben mit einem Reverse-Proxy (NGINX auf FreeBSD) auf verschiedene gestartete Services verteilt. Das ganze ist nicht ganz einfach zu handeln weil es doch unübersichtlich ist und langsam wächst auch die Kritik an diesem Aufbau weil die Server viele unbenutzte Cores und RAM haben und die Wartezeit doch zu hoch wird wenn z.B. gleichzeitig PDF im gleichen Service erstellt werden müssen und gleichzeitig über 90% des Servers unbenutzt rumsteht .....

Sicher, mein Programm ist ein Relikt aus vergangenen Zeiten und ich verstehe auch die Kritik der Berater am Aufbau aber irgendwie sollte man das doch in die Gegenwart konvertieren können, am besten auch gleich Plattformunabhängig ohne alles neu zu schreiben..... was würdest du tun?
Valar Morghulis

Gruss Carlo
Benutzeravatar
azzo
Rekursionen-Architekt
Rekursionen-Architekt
Beiträge: 496
Registriert: So, 28. Mär 2010 19:21
Danksagung erhalten: 14 Mal

Re: Ist Transformation von Xbase++ in JAVA möglich?

Beitrag von azzo »

Hallo Carlo,
nur um eine Idee zu haben, wie viele Nutzer hat das Programm gleichzeitig und wie groß sind die Datenmengen? Was sind das für PDFs? Rechnungen oder umfangreiche PDFs?

MfG
Otto
Antworten