Ressourcencontainer

    • Offizieller Beitrag

    Hi,
    mit FileInstall oder Speichern des Binärcodes von Ressourcen direkt im Skript kann ich nur eine vorher festgelegte Quantität und Qualität dieser Ressourcen verwenden. D.h., wenn ich z.B. ein Bild aktualisieren möchte, muß das Skript dann auch neu kompiliert werden.
    Kann man natürlich umgehen, wenn man die Ressourcen auslagert. Dabei ist es jedoch nicht sehr schick, wenn eine Handvoll Dateien im Verzeichnis "rumlungern". Mit nur einer ist das doch viel eleganter.
    Und um es so einfach, wie möglich handhaben zu können, sollte es auch nicht unbedingt in einer Dll landen.
    Deshalb habe ich zwei Tools erstellt, einmal zum Einfügen/Austauschen von Ressourcen in einem Ressourcencontainer und einmal um gezielt Ressourcen aus diesem Container in eine Zieldatei zu installieren.
    Das Erstellen des Containers kann per Kommandozeile oder GUI erfolgen. Bei der GUI-Variante ist sowohl Drag&Drop als auch Auswahl per FileOpenDialog (beides mit Mehrfachauswahl) möglich.
    Soll eine Komponente aktualisiert werden: Container aufrufen mit der Komponente und ein Überschreibflag setzen.
    Dateiname.Suffix der eingefügten Komponenten sind auch die Identifier um die jeweilige Komponente wieder abzurufen.
    Es wird bei jedem Erstellen/Hinzufügen von Komponenten auf eventuell bereits vorhandene Doppel dieser Komponenten geprüft. Ist das Überschreibflag nicht gesetzt, bleiben die vorhandenen Versionen erhalten.
    Wenn ihr mal vergessen habt, was eigentlich im Container enthalten ist: Mit "SourceFromContainer.exe <Ressourcendatei> LIST" werden die Identifier der Komponenten ausgelesen und in die Zwischenablage übergeben.
    Für den Container verwende ich das Suffix *.rbn ( ressourcen binär ). Eine Ressourcencontainer ist folgendermaßen aufgebaut:

    Code
    <Dateiname.Suffix>
    Binärstring_dieser_Datei
    </Name.Suffix>


    Das heißt also auch: NIEMALS eine Containerdatei mit SciTE öffnen! Bei kleineren Dateien mag das noch gehen, aber schon unter 100 kB ist der Binärstring deutlich länger als das, was SciTE verkraften kann. Es schmiert dann einfach ab.

    Es spielt keine Rolle, was ihr in den Container packt. Bilder, Texte für Controls, Sounddateien - alles kann rein. Nur sollte man das nicht als Bibliothek oder Mediathek betrachten. Dazu ist es nicht gedacht. Das kleine Gerümpel, das sonst im Programmverzeichnis dümpelt, soll einen Ruheplatz finden. ;)

    Erstellen eines Ressourcen Containers per Kommandozeile:

    Spoiler anzeigen
    [autoit]

    $sPathRessource = "C:\Users\Standard\Code_AutoIt\TEST\Ressourcencontainer\RessContainer1.rbn"
    $sPathDigi = 'C:\Pict\digicam_blau_200x150.jpg'
    $sPathNPP = 'C:\Pict\Ico\npp_64x64.ico'
    $sPathVideo = 'C:\Pict\Ico\videocam_87x96.ico'

    [/autoit] [autoit][/autoit] [autoit]

    RunWait("C:\Tools\SourceToContainer.exe " & $sPathRessource & " " & $sPathDigi & " " & $sPathNPP & " " & $sPathVideo)

    [/autoit]
    SourceToContainer(0.1) Kommandozeile und GUI
    [autoit]

    #Region - TimeStamp
    ; 2011-11-17 00:10:37 v 0.1
    #EndRegion - TimeStamp
    #include-once
    #include <GUIConstantsEx.au3>
    #include <WindowsConstants.au3>

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    #cs
    MUSTER AUFRUF:
    "..\SourceToContainer.exe <Ressourcendatei> <Quelldatei_1> <Quelldatei_2..n> [ optional: Überschreibflag = 1 ]

    [/autoit] [autoit][/autoit] [autoit]

    "..\SourceToContainer.exe C:\bla\Ressource.rbn C:\Pict\picture1.jpg C:\Pict\picture2.jpg C:\Pict\picture3.jpg"
    mit Überschreib-Flag
    "..\SourceToContainer.exe C:\bla\Ressource.rbn C:\Pict\picture2.jpg 1"
    #ce
    ;===============================================================================
    ; Script Name......: SourceToContainer.au3
    ; Description......: Quelldatei(en) als Binärstring in eine Ressourcendatei schreiben (neu erstellen od. hinzufügen/überschreiben), Dateityp (*.rbn) (RessourceBiNär)
    ; Dateiname.Suffix als Identifier
    ; Existiert eine Ressource bereits, wird diese bei gesetztem Überschreib-Flag überschrieben. Ohne Flag bleibt die vorhandene Version erhalten.
    ; Mit SourceFromContainer.au3 wird aus der Ressourcendatei dann die gewünschte Quelldatei wieder ausgelesen und installiert.
    ; AutoIt version...: 3.3.6.1
    ; Author(s)........: BugFix ( [email='bugfix@autoit.de'][/email] )
    ;===============================================================================

    [/autoit] [autoit][/autoit] [autoit]

    Global $sPathRess, $aFiles[1], $fOverwrite = False

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    ; == Kommandozeilen-Variante
    If $CMDLINE[0] Then ; == [1]=Pfad_Ressourcendatei; [2..[0]-1]=Pfade_Quelldateien; [[0]-1]=optional_Flag_Overwrite(1) - Default nicht gesetzt
    $sPathRess = $CMDLINE[1]
    ReDim $aFiles[$CMDLINE[0] -1]
    For $i = 2 To $CMDLINE[0]
    $aFiles[$i-2] = $CMDLINE[$i]
    If $CMDLINE[$i] = 1 Then
    $fOverwrite = True
    ReDim $aFiles[$CMDLINE[0] -2]
    ExitLoop
    EndIf
    Next
    __AddOrCreateBin($sPathRess, $aFiles, $fOverwrite)
    Exit
    EndIf

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    ; == GUI-Variante
    $hMain = GUICreate(' Ressourcencontainer Erstellen oder Dateien Hinzufügen', 600, 190, -1, -1, -1, $WS_EX_ACCEPTFILES)
    GUICtrlCreateGroup(' Quelldatei(en) Pfad(e) ', 10, 10, 580, 60)
    $inSource = GUICtrlCreateInput('', 20, 35, 525, 20)
    GUICtrlSetState(-1, $GUI_DROPACCEPTED)
    $btSource = GUICtrlCreateButton('...', 555, 35, 25, 20)
    GUICtrlCreateGroup('', -99, -99, 1, 1)
    GUICtrlCreateGroup(' Ressourcencontainer [ Zieldatei (*.rbn) ] ', 10, 80, 580, 60)
    $inDest = GUICtrlCreateInput('', 20, 105, 525, 20)
    GUICtrlSetState(-1, $GUI_DROPACCEPTED)
    $btDest = GUICtrlCreateButton('...', 555, 105, 25, 20)
    GUICtrlCreateGroup('', -99, -99, 1, 1)
    $btRun = GUICtrlCreateButton('Start', 500, 155, 80, 20)
    $cbOverwrite = GUICtrlCreateCheckbox(' Ressourcen, wenn bereits vorhanden, überschreiben', 20, 158, 300, 17)

    [/autoit] [autoit][/autoit] [autoit]

    GUISetState()
    WinSetOnTop($hMain, '', 1)

    [/autoit] [autoit][/autoit] [autoit]

    While 1
    Switch GUIGetMsg()
    Case -3
    Exit
    Case $btSource
    WinSetOnTop($hMain, '', 0)
    $sRead = FileOpenDialog('Quelldatei(en)', @CommonFilesDir, 'Alle (*.*)', 7)
    If $sRead <> '' Then
    $aTmp = StringSplit($sRead, '|')
    If $aTmp[0] = 1 Then
    GUICtrlSetData($inSource, $sRead)
    Else
    $sRead = ''
    For $i = 2 To $aTmp[0]
    $sRead &= $aTmp[1] & '\' & $aTmp[$i] & '|'
    Next
    GUICtrlSetData($inSource, StringTrimRight($sRead, 1))
    EndIf
    EndIf
    WinSetOnTop($hMain, '', 1)
    Case $btDest
    WinSetOnTop($hMain, '', 0)
    $sRead = FileOpenDialog('Ressourcendatei', @CommonFilesDir, 'Ressource Binär (*.rbn)', 10)
    If $sRead <> '' Then
    If StringRight($sRead, 4) <> '.rbn' Then $sRead &= '.rbn'
    GUICtrlSetData($inDest, $sRead)
    EndIf
    WinSetOnTop($hMain, '', 1)
    Case $btRun
    If GUICtrlRead($inSource) <> '' Then
    $aFiles = StringSplit(GUICtrlRead($inSource), '|', 2)
    If GUICtrlRead($inDest) <> '' Then
    $sPathRess = GUICtrlRead($inDest)
    If BitAND(GUICtrlRead($cbOverwrite), $GUI_CHECKED) Then $fOverwrite = True
    __AddOrCreateBin($sPathRess, $aFiles, $fOverwrite)
    Else
    GUICtrlSetData($inDest, 'D E S T I N A T I O N !!!')
    Sleep(600)
    GUICtrlSetData($inDest, '')
    EndIf
    Else
    GUICtrlSetData($inSource, 'S O U R C E !!!')
    Sleep(600)
    GUICtrlSetData($inSource, '')
    EndIf
    EndSwitch
    WEnd

    [/autoit] [autoit][/autoit] [autoit]

    Func __AddOrCreateBin($sPathRess, ByRef $aFiles, $fOverwrite=False)
    Local $hFile, $bRead, $sToWrite = '', $sFileName, $sFileErr = '', $sMsg = "erstellt."
    Local $n = UBound($aFiles), $nErr = 0, $aContent, $aExisting[1][2] = [[0]], $ret, $x
    If StringRight($sPathRess, 4) <> '.rbn' Then $sPathRess &= '.rbn'
    If FileExists($sPathRess) Then
    $sToWrite = FileRead($sPathRess)
    $sMsg = "erweitert."
    ; == Check ob Dateien bereits vorhanden
    $aContent = StringSplit($sToWrite, @CRLF, 1)
    For $i = 0 To UBound($aFiles) -1
    $ret = __RessourceGetLine($aContent, __GetFileName($aFiles[$i]))
    If $ret > 0 Then
    $aExisting[0][0] += 1
    ReDim $aExisting[$aExisting[0][0] +1][2]
    $aExisting[$aExisting[0][0]][0] = $i
    $aExisting[$aExisting[0][0]][1] = $ret
    EndIf
    Next
    ; == wenn Überschreibmodus und Doppel vorhanden ==> Doppel löschen durch Kopieren bleibender Inhalte in $sToWrite
    If $fOverwrite And $aExisting[0][0] > 0 Then
    Local $sTmpWrite = ''
    For $i = 1 To $aContent[0] -3 Step 3
    $x = 0
    For $j = 1 To $aExisting[0][0]
    If $aExisting[$j][1] = $i Then
    $x = 1
    ExitLoop
    EndIf
    Next
    If $x = 0 Then
    $sTmpWrite &= $aContent[$i] & @CRLF & $aContent[$i+1] & @CRLF & $aContent[$i+2] & @CRLF
    EndIf
    Next
    $sToWrite = $sTmpWrite
    EndIf
    EndIf
    For $i = 0 To UBound($aFiles) -1
    If Not FileExists($aFiles[$i]) Then
    $sFileErr &= $aFiles[$i] & @CRLF
    $nErr += 1
    ContinueLoop
    EndIf
    If $aExisting[0][0] > 0 Then
    $x = 0
    For $j = 1 To $aExisting[0][0]
    If $aExisting[$j][0] = $i Then
    $x = 1
    ExitLoop
    EndIf
    Next
    If ($x = 1) And (Not $fOverwrite) Then ; == Doppel u. kein $fOverwrite ==> nicht hinzufügen
    $n -= 1 ; == Dateizähler für Msg
    ContinueLoop
    EndIf
    EndIf
    $hFile = FileOpen($aFiles[$i], 16) ; == es existieren Doppel, wenn $fOverwrite, wurden diese gelöscht ==> müssen neu hinzugefügt werden
    $bRead = FileRead($hFile)
    FileClose($hFile)
    $sFileName = __GetFileName($aFiles[$i])
    $sToWrite &= '<' & $sFileName & '>' & @CRLF & _ ; == <Name.Suffix>
    $bRead & @CRLF & _ ; == Binärstring
    '</' & $sFileName & '>' & @CRLF ; == </Name.Suffix>
    Next
    If $sToWrite <> '' Then
    $hFile = FileOpen($sPathRess, 2)
    FileWrite($hFile, $sToWrite)
    FileClose($hFile)
    EndIf
    If Not $nErr Then
    MsgBox(262208,"Ressourcen Container","Die Ressourcendatei " & @CRLF & $sPathRess & @CRLF & "wurde mit " & $n & " Datei(en) " & $sMsg)
    Else
    $sMsg = "Von " & $n & " Datei(en) waren " & $nErr & " Datei(en) unter dem angegebenen Pfad nicht vorhanden:" & @CRLF & $sFileErr & @CRLF & "Die Ressourcen Datei wurde "
    Select
    Case ($sToWrite = '') And ($n = $nErr)
    $sMsg &= 'nicht erstellt!'
    Case $sToWrite = ''
    $sMsg &= 'ohne die angeführte(n) Datei(en) erstellt!'
    Case ($sToWrite <> '') And ($n = $nErr)
    $sMsg &= 'nicht erweitert!'
    Case $sToWrite <> ''
    $sMsg &= 'ohne die angeführte(n) Datei(en) erweitert!'
    EndSelect
    MsgBox(262192,"Ressourcen Datei - Fehler", $sMsg)
    EndIf
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func __GetFileName($sFullPath)
    Return StringTrimLeft($sFullPath, StringInStr($sFullPath, '\', 1, -1))
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func __RessourceGetLine(ByRef $aContent, $sSearch)
    Local $iLine = 0
    For $i = 1 To $aContent[0] Step 3
    If $aContent[$i] = '<' & $sSearch & '>' Then
    $iLine = $i
    ExitLoop
    EndIf
    Next
    Return $iLine
    EndFunc

    [/autoit]

    Dateien aus dem Ressourcen Container in das TempDir installieren:

    Spoiler anzeigen
    [autoit]

    $sPathRessource = "C:\Users\Standard\Code_AutoIt\TEST\Ressourcencontainer\RessContainer1.rbn"
    $sFileDigi = 'digicam_blau_200x150.jpg'
    $sFileNPP = 'npp_64x64.ico'
    $sFileVideo = 'videocam_87x96.ico'

    [/autoit] [autoit][/autoit] [autoit]

    RunWait("C:\Tools\SourceFromContainer.exe " & $sPathRessource & " " & $sFileDigi & " " & @TempDir & "\" & $sFileDigi)
    RunWait("C:\Tools\SourceFromContainer.exe " & $sPathRessource & " " & $sFileNPP & " " & @TempDir & "\" & $sFileNPP)
    RunWait("C:\Tools\SourceFromContainer.exe " & $sPathRessource & " " & $sFileVideo & " " & @TempDir & "\" & $sFileVideo)

    [/autoit]
    SourceFromContainer(0.1) Kommandozeile
    [autoit]

    #Region - TimeStamp
    ; 2011-11-17 00:09:59 v 0.1
    #EndRegion - TimeStamp

    [/autoit] [autoit][/autoit] [autoit]

    #cs
    MUSTER AUFRUF:
    "..\SourceFromContainer.exe <Ressourcendatei> <Identifier-Ressource> <Installationspfad>"

    [/autoit] [autoit][/autoit] [autoit]

    "..\SourceFromContainer.exe C:\bla\Ressource.rbn picture1.jpg " & @TempDir & "\picture1.jpg"

    [/autoit] [autoit][/autoit] [autoit]

    AUFLISTUNG der Ressourcen eines Containers:
    "..\SourceFromContainer.exe <Ressourcendatei> LIST"

    [/autoit] [autoit][/autoit] [autoit]

    "..\SourceFromContainer.exe C:\bla\Ressource.rbn LIST"
    schreibt Ressourcen-Identifier in die Zwischenablage:
    Pict1.jpg
    Message1.txt
    Program1.ico
    #ce

    [/autoit] [autoit][/autoit] [autoit]

    ;===============================================================================
    ; Script Name......: SourceFromContainer.au3
    ; Description......: Binärstring (Dateiname.Suffix als Identifier) aus Ressourcendatei lesen und damit Zieldatei erstellen (neu erstellen od. hinzufügen/überschreiben)
    ; Ressourcencontainer [Dateityp (*.rbn)] muß mit SourceToContainer.au3 erstellt worden sein
    ; Ressourcen Identifier eines Containers lassen sich mit LIST in die Zwischenablage ausgeben
    ; AutoIt version...: 3.3.6.1
    ; Author(s)........: BugFix ( [email='bugfix@autoit.de'][/email] )
    ;===============================================================================

    [/autoit] [autoit][/autoit] [autoit]

    If $CMDLINE[0] Then ; == [1]=Pfad_Ressourcendatei; [2]=zu_verwendende_Datei_aus_dem_Container; [3]=Installationspfad
    Local $sPathRess, $sFileName, $sPathInstall, $aContent, $hFileOut, $sRessourcen = ''
    $sPathRess = $CMDLINE[1]
    If Not FileExists($sPathRess) Then Exit MsgBox(262192, 'Fehler', 'Quelldatei nicht vorhanden!')
    $sFileName = $CMDLINE[2]
    If $sFileName <> 'LIST' Then $sPathInstall = $CMDLINE[3]
    $aContent = StringSplit(FileRead($sPathRess) , @CRLF, 1)
    For $i = 1 To $aContent[0] -3 Step 3
    If $sFileName = 'LIST' Then
    $sRessourcen &= StringTrimLeft(StringTrimRight($aContent[$i], 1) , 1) & @CRLF
    ContinueLoop
    EndIf
    If $aContent[$i] = '<' & $sFileName & '>' Then
    $hFileOut = FileOpen($sPathInstall, 2+8+16)
    FileWrite($hFileOut, Binary($aContent[$i+1]))
    FileClose($hFileOut)
    Exit
    EndIf
    Next
    If $sFileName = 'LIST' Then Exit ClipPut($sRessourcen)
    Exit MsgBox(262192, 'Fehler', '"' & $sFileName '" nicht im Ressourcencontainer enthalten!')
    EndIf

    [/autoit]

    Edit 17.11.2011:
    Hier mal noch ein Muster, wie man ein Skript gestalten kann, um die Ressourcen mit einem einfachen Kommandozeilenaufruf upzudaten oder zu erweitern.

    Spoiler anzeigen
    [autoit]

    ; == die Tools per FileInstall im Skript verfügbar machen
    FileInstall("C:\Tools\SourceToContainer.exe", @TempDir & "\SourceToContainer.exe")
    FileInstall("C:\Tools\SourceFromContainer.exe", @TempDir & "\SourceFromContainer.exe")
    Local $STCexe = @TempDir & "\SourceToContainer.exe" ; == für Updates und Hinzufügen von Ressourcen
    Local $SFCexe = @TempDir & "\SourceFromContainer.exe" ; == zum Laden der Ressourcen

    [/autoit] [autoit][/autoit] [autoit]

    ; == Pfad Ressourcencontainer
    Local $sPathRessource = @ScriptDir & "\RessContainer1.rbn"

    [/autoit] [autoit][/autoit] [autoit]

    ; == UPDATEN von Ressourcen, Kommandozeilenaufruf: "Skript.exe UPDATE <Pfad_zur_neuen_Version_von_Datei_1> <Pfad_zur_neuen_Version_von_Datei_2...n>"
    ; == HINZUFÜGEN von neuen Ressourcen, analog: "Skript.exe ADD <Pfad_zur_Datei_X> <Pfad_zur_Datei_Y...n>"
    If $CMDLINE[0] Then
    If StringInStr('UPDATE ADD', $CMDLINE[1]) Then
    Local $sCmd = $STCexe & " " & $sPathRessource
    For $i = 2 To $CMDLINE[0]
    $sCmd &= " " & $CMDLINE[$i]
    Next
    If $CMDLINE[1] = 'UPDATE' Then $sCmd &= " 1"
    RunWait($sCmd)
    EndIf
    EndIf

    [/autoit] [autoit][/autoit] [autoit]

    ; == alle Identifier des Containers in Zwischenablage laden und als Array speichern
    RunWait($SFCexe & " " & $sPathRessource & " LIST")
    Local $aIdentifier = StringSplit(ClipGet(), @CRLF, 1)

    [/autoit] [autoit][/autoit] [autoit]

    ; == alle Ressourcen des Containers im TempDir installieren
    For $i = 1 To $aIdentifier[0]
    RunWait($SFCexe & " " & $sPathRessource & " " & $aIdentifier[$i] & " " & @TempDir & "\" & $aIdentifier[$i])
    Next

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    #region - Skript
    ; ....
    ; ....
    ; ....
    #endregion

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    ; == bei Beenden Skript sollte man die Dateien wieder löschen
    For $i = 1 To $aIdentifier[0]
    FileDelete(@TempDir & "\" & $aIdentifier[$i])
    Next
    FileDelete($STCexe)
    FileDelete($SFCexe)

    [/autoit]
  • Schöne Idee, ich habs noch nie versucht eine Datei in eine Dll zu packen, aber ist es wirklich so schwer? Bzw ist es schwerer als das was du da gemacht hast ^^

    Du meintest das es sinnvoll ist seine Dateien auszulagern damit man nicht immer die .Exe neumachen muss, aber so muss man doch immer die .rbn neugeladen werden und wenn z.B. 20 Bilder, Musik etc drin ist dann wäre es doch schlecht die alle nochmal neu zu laden oder nicht?

    Ich hab mir deinen Code etc noch nicht angeschaut (Nur den Thread gelesen und das wodrums geht) doch was wäre wenn aus Zufall in dem Binärcode <xx.xx> drinstehen würde, dann würde, das was sicherlich durch StringRegExp returnt wird, durcheinander gebracht werden oder nicht?

    mfg BB

    "IF YOU'RE GOING TO KILL IT
    OPEN SOURCE IT!"

    by Phillip Torrone

    Zitat von Shoutbox

    [Heute, 11:16] Andy: ....böseböseböseböse....da erinnere ich mich daran, dass man den Puschelschwanz eines KaRnickels auch "Blume" nennt....ob da eins zum anderen passt? :rofl: :rofl: :rofl: :rofl:

    https://autoit.de/index.php?page…leIt#post251138

    Neon Snake

    • Offizieller Beitrag

    ich habs noch nie versucht eine Datei in eine Dll zu packen, aber ist es wirklich so schwer? Bzw ist es schwerer als das was du da gemacht hast

    Es ist nicht schwer Daten mit z.B. Ressouce-Editor in einer Dll zu verpacken - aber versuch das mal dynamisch zu machen, sprich bei deinem User im installierten Programm. Das geht in die Hose.
    Wenn du z.B. ein Programm hast, das diverse Module verwaltet und für jedes Modul gibt es z.B. eine Bild auf der GUI, dann mußt du für ein neues Modul auch ein neues Bild nachladen können. Das macht dein Programm dynamisch.

    aber so muss man doch immer die .rbn neugeladen werden und wenn z.B. 20 Bilder, Musik etc drin ist dann wäre es doch schlecht die alle nochmal neu zu laden oder nicht

    Mann muß doch nur das aktualisieren, was erneuert oder neu eingefügt werden soll.

    doch was wäre wenn aus Zufall in dem Binärcode <xx.xx> drinstehen würde, dann würde, das was sicherlich durch StringRegExp returnt wird, durcheinander gebracht werden oder nicht

    Das Programm generiert den Binärcode aus den angegeben Dateien, wie soll dann solch Mist drin stehen? :D

  • Nettes Skript,
    aber ich finde DLLs immer noch am praktischsten und am kompatibelsten.
    Über _WinAPI_BeginUpdateResource, _WinAPI_UpdateResource und _WinAPI_EndUpdateResource können auch relativ einfach Resourcen einer PE-Datei (DLL, EXE) gelöscht, geändert oder hinzugefügt werden.