none
Es werden nicht alle Mails beim ersten Anlauf gelöscht, loop schafft derzeit abhilfe RRS feed

  • Frage

  • Hallo zusammen,

    ich habe ein kleines Script geschrieben, um Mails aus Clientpostfächern zu löschen.
    Solange das Ganze an einer Mail im Quellordner getestet wird funktioniert alles wunderbar.
    Sobald die Anzahl jedoch steigt werden nicht mehr alle Mails beim ersten Anlauf gelöscht, sondern der Prozess muss mehrmals gestartet werden.

    Das Script sieht wie folgt aus:

    ##################################################################################################################################################################################################################################################################################################################################################################################
    # Script zum löschen der gesendeten Elemente im Postfach der x-Anwender von Absender x mit Bearbeitungscode x
    # Stand: 15.03.2018
    ##################################################################################################################################################################################################################################################################################################################################################################################
    $ErrorActionPreference = "SilentlyContinue"
    $WarningActionPreference = "SilentlyContinue"
        # Datum für Logdatei setzen
    $dateforlog = get-date -format ddMMyy
        # Dateiname für Logdatei generieren
    $logfilesentitems = "C:\log\deleteSent("+$dateforlog+").txt"
        # Startzeit des Scripts ermitteln
    $starttime = get-date -format hh:mm:ss
        # Einbinden von Outlook
    $outlook = new-object -com outlook.application
        #Setzen des Ordnernamens
    $global:SENT = "Gesendete Elemente"
        # Log-Header & Startzeit
    "Datum"+";"+"Uhrzeit"+";"+"Anwender"+";"+"Betreff" >> $logfilesentitems
    "$dateforlog"+";"+$starttime+";"+"Administrator"+";"+"Startzeitpunkt des Scripts" >> $logfilesentitems
        # Filtern der betroffenen Benutzer des VKI
    $USERS = get-aduser -filter * -Properties * -SearchBase "OU der betroffenen Nutzer" | ? {($_.surname -notlike "" -and $_.givenname -notlike "" -and ($_.physicalDeliveryOfficeName -like "WERT" -or $_.physicalDeliveryOfficeName -like "WERT") -and ($_.Description -notmatch "WERT"))} | sort surname
        # Einbinden der benötigten Werte
    foreach($USER in $USERS){
        # Postfach auf das zugegriffen wird muss als Displayname eingetragen werden
        $USER = $USER | select -ExpandProperty displayname
        $mailbox = $outlook.Session.Folders.Item($USER).Folders.Item($SENT).Items
        foreach($mail in $mailbox){
        # Herausfiltern von Kalendereinträgen. Nur Mails sollen gelöscht werden
            $mclass = $mail | select -ExpandProperty MessageClass
            IF($mclass -match "Schedule"){}
            ELSE{
        # laden der Werte Subject und Absender in je eine eigene Variable
                $subject = $mail | select -ExpandProperty Subject
                $sender = $mail | select -ExpandProperty SentOnBehalfOfName
        # Abfrage auf Bearbeitungscode und Absender
                IF($subject -match "WERT" -and $sender -match "WERT"){
        # Datum & Uhrzeit für logging
                    $dd = get-date -format dd.MM.yy
                    $dh = get-date -format hh:mm:ss
                    #$subject
                    $message = $dd+";"+$dh+";"+$USER+";"+$subject
                    $log += $message
        
        
        
        # Löschen der gefilterten Mail fünf Durchläufe, da der erste nicht jede Mail erwischt
                    for($i = 0; $i -lt 5; $i++){
                        $mail.Delete()
                    }
                    Clear-Variable log, message, dd, dh
                    $log >> $logfilesentitems
                }
                ELSE{}
        
        
        
        
        # Löschen der im Verlauf eingesetzten Variablen - keep your ENV cleaned up
            Remove-Variable subject, sender 
            }
        Remove-Variable mail, mclass
        }
    Remove-Variable mailbox
    }
        # Schreiben des Endzeitpunkts ins log
    $ENDZEIT = get-date -format hh:mm:ss
    "$dateforlog"+";"+$ENDZEIT+";"+"Administrator"+";"+"Endzeitpunkt des Scripts" >> $logfilesentitems
        # Löschen aller eingesetzten Variablen - keep your ENV cleaned up
    Remove-Variable outlook, sent, i, VKIuser, VKIusers, dateforlog, logfilesentitems, starttime, ENDZEIT, WarningActionPreference, ErrorActionPreference
    exit

    Im Eigentlichen geht es um folgenden Block:

                    for($i = 0; $i -lt 5; $i++){
                        $mail.Delete()
                    }
                    Clear-Variable log, message, dd, dh
                    $log >> $logfilesentitems
                }
                ELSE{}
        

    Gibt es eine Methode, welche bei Durchführung des .Delete auf Erfolg wartet?

    Ich bin für jeden zielführenden Hinweis dankbar.

    Viele Grüße

    Donnerstag, 15. März 2018 11:57

Antworten

  • Moin,

    ich habe schon lange nichts mehr für Outlook gehackt, dafür aber früher (so bis inkl. OL2013) eine ganze Menge, und es war schon immer so. Zuverlässige Abhilfe konnte in meiner Erfahrung immer nur mit zusätzlichen Bibliotheken geschaffen werden:

    • entweder Redemption
    • oder EWS Managed MAPI und direkt an Exchange rangehen, wenn Exchange 2010+ dahinter ist.


    Evgenij Smirnov

    I work @ msg services ag, Berlin -> http://www.msg-services.de
    I blog (in German) @ http://it-pro-berlin.de
    my stuff in PSGallery --> https://www.powershellgallery.com/profiles/it-pro-berlin.de/
    Exchange User Group, Berlin -> https://exusg.de
    Windows Server User Group, Berlin -> http://www.winsvr-berlin.de
    Mark Minasi Technical Forum, reloaded -> http://newforum.minasi.com


    In theory, there is no difference between theory and practice. In practice, there is.

    ich habe gerade was im Technet gefunden, was mir sinnig erscheint.

    Link: Link

    Nach Umbau des bei mir eingesetzten Foreach auf das im Link beschriebene Vorgehen mit For funktioniert es nun beim ersten Durchlauf :-)
    Wenn Anzahl Mail = 10 ist der Counter auf 10 und er kann die Mail mit Index 1 beim ersten Durchlauf löschen.
    Bei Durchlauf 6 soll Mail 6 mit dem neuen Index 1 gelöscht werden, da Mail 1-5 den Index je um 1 verringert haben.

    Verworren das Ganze, aber macht irgendwie schon Sinn.

    Danke dennoch für die Antwort! Kommentare von Herrn Smirnov lese ich nur zu gern in meinen Threads! ;-)

    Finds' immer lustig. Ich poste nen Eintrag im Technet, weil die Suche nichts ergeben hat, Zack keine Minute später gibts bei der Suche ein Ergebnis.




    Donnerstag, 15. März 2018 12:14

Alle Antworten

  • Moin,

    ich habe schon lange nichts mehr für Outlook gehackt, dafür aber früher (so bis inkl. OL2013) eine ganze Menge, und es war schon immer so. Zuverlässige Abhilfe konnte in meiner Erfahrung immer nur mit zusätzlichen Bibliotheken geschaffen werden:

    • entweder Redemption
    • oder EWS Managed MAPI und direkt an Exchange rangehen, wenn Exchange 2010+ dahinter ist.


    Evgenij Smirnov

    I work @ msg services ag, Berlin -> http://www.msg-services.de
    I blog (in German) @ http://it-pro-berlin.de
    my stuff in PSGallery --> https://www.powershellgallery.com/profiles/it-pro-berlin.de/
    Exchange User Group, Berlin -> https://exusg.de
    Windows Server User Group, Berlin -> http://www.winsvr-berlin.de
    Mark Minasi Technical Forum, reloaded -> http://newforum.minasi.com


    In theory, there is no difference between theory and practice. In practice, there is.

    Donnerstag, 15. März 2018 12:11
  • Moin,

    ich habe schon lange nichts mehr für Outlook gehackt, dafür aber früher (so bis inkl. OL2013) eine ganze Menge, und es war schon immer so. Zuverlässige Abhilfe konnte in meiner Erfahrung immer nur mit zusätzlichen Bibliotheken geschaffen werden:

    • entweder Redemption
    • oder EWS Managed MAPI und direkt an Exchange rangehen, wenn Exchange 2010+ dahinter ist.


    Evgenij Smirnov

    I work @ msg services ag, Berlin -> http://www.msg-services.de
    I blog (in German) @ http://it-pro-berlin.de
    my stuff in PSGallery --> https://www.powershellgallery.com/profiles/it-pro-berlin.de/
    Exchange User Group, Berlin -> https://exusg.de
    Windows Server User Group, Berlin -> http://www.winsvr-berlin.de
    Mark Minasi Technical Forum, reloaded -> http://newforum.minasi.com


    In theory, there is no difference between theory and practice. In practice, there is.

    ich habe gerade was im Technet gefunden, was mir sinnig erscheint.

    Link: Link

    Nach Umbau des bei mir eingesetzten Foreach auf das im Link beschriebene Vorgehen mit For funktioniert es nun beim ersten Durchlauf :-)
    Wenn Anzahl Mail = 10 ist der Counter auf 10 und er kann die Mail mit Index 1 beim ersten Durchlauf löschen.
    Bei Durchlauf 6 soll Mail 6 mit dem neuen Index 1 gelöscht werden, da Mail 1-5 den Index je um 1 verringert haben.

    Verworren das Ganze, aber macht irgendwie schon Sinn.

    Danke dennoch für die Antwort! Kommentare von Herrn Smirnov lese ich nur zu gern in meinen Threads! ;-)

    Finds' immer lustig. Ich poste nen Eintrag im Technet, weil die Suche nichts ergeben hat, Zack keine Minute später gibts bei der Suche ein Ergebnis.




    Donnerstag, 15. März 2018 12:14
  • ...aber Du machst ja keinen Count, sondern Foreach, was sich für eine Collection auch gehört.

    Evgenij Smirnov

    I work @ msg services ag, Berlin -> http://www.msg-services.de
    I blog (in German) @ http://it-pro-berlin.de
    my stuff in PSGallery --> https://www.powershellgallery.com/profiles/it-pro-berlin.de/
    Exchange User Group, Berlin -> https://exusg.de
    Windows Server User Group, Berlin -> http://www.winsvr-berlin.de
    Mark Minasi Technical Forum, reloaded -> http://newforum.minasi.com


    In theory, there is no difference between theory and practice. In practice, there is.

    Donnerstag, 15. März 2018 12:16
  • >      $mailbox = $outlook.Session.Folders.Item($USER).Folders.Item($SENT).Items

    Hätte dazu noch was interessantes gefunden: "Zum Löschen aller Elemente in der Items -Auflistung eines Ordners müssen Sie jedes Element löschen, beginnend mit dem letzten Element im Ordner."
    https://msdn.microsoft.com/de-de/VBA/Outlook-VBA/articles/mailitem-delete-method-outlook

    Würde bedeuten, daß Du nicht mit foreach durchlaufen kannst, sondern mit einer for-Schleife. Und da dann rückwärts zählen mußt.

    Donnerstag, 15. März 2018 14:59
  • Würde bedeuten, daß Du nicht mit foreach durchlaufen kannst, sondern mit einer for-Schleife. Und da dann rückwärts zählen mußt.

    ...und aus genau diesem Grund funktioniert das nicht zuverlässig, wenn nicht jedes Element gelöscht werden soll...

    Evgenij Smirnov

    I work @ msg services ag, Berlin -> http://www.msg-services.de
    I blog (in German) @ http://it-pro-berlin.de
    my stuff in PSGallery --> https://www.powershellgallery.com/profiles/it-pro-berlin.de/
    Exchange User Group, Berlin -> https://exusg.de
    Windows Server User Group, Berlin -> http://www.winsvr-berlin.de
    Mark Minasi Technical Forum, reloaded -> http://newforum.minasi.com


    In theory, there is no difference between theory and practice. In practice, there is.

    Donnerstag, 15. März 2018 15:06
  • Wenn eine Collection mit ForEach durchlaufen wird und Einträge entfernt oder hinzugefügt werden, verschiebt sich natürlich alles.
    In .Net gibt es daher eine Exception, dass die Auflistung (Collection) geändert wurde, der ForEach wird dann abgebrochen (da die PowerShell ja eigentlich .Net-basiert ist, frage ich mich allerdings warum das nicht passiert).

    Nun gibt es 2 Verfahren:
    - Wie oben bereits gesagt, die Collection rückwärts per For-Schleife durcharbeiten.
    - Per ForEach die Enträge ermitteln und in einer eigenen Liste sammeln, im 2. Schritt die eigene Liste abarbeiten um dann die gefundenen Einträge zu löschen

    Wichtig ist natürlich, dass während der Bearbeitung kein anderer Prozess an der Collection rumfummelt.

    Donnerstag, 15. März 2018 15:14
  • Also das nun fertige Script sieht wie folgt aus:

    ##################################################################################################################################################################################################################################################################################################################################################################################
    # Script zum löschen der gesendeten Elemente im Postfach der x-Anwender von Absender x mit Bearbeitungscode x*
    # Stand: 15.03.2018
    ##################################################################################################################################################################################################################################################################################################################################################################################
    $ErrorActionPreference = "SilentlyContinue"
    $WarningActionPreference = "SilentlyContinue"
        # Datum für Logdatei setzen
    $dateforlog = get-date -format ddMMyy
        # Dateiname für Logdatei generieren
    $logfilesentitems = "C:\log\deleteSent("+$dateforlog+").txt"
        # Startzeit des Scripts ermitteln
    $starttime = get-date -format HH:mm:ss
        # Einbinden von Outlook
    $outlook = new-object -com outlook.application
        #Setzen des Ordnernamens
    $global:SENT = "Gesendete Elemente"
        # Log-Header & Startzeit
    "Datum"+";"+"Uhrzeit"+";"+"Anwender"+";"+"Betreff" >> $logfilesentitems
    "$dateforlog"+";"+$starttime+";"+"Administrator"+";"+"Startzeitpunkt des Scripts" >> $logfilesentitems
        # Filtern der betroffenen Benutzer des VKI
    $Users = get-aduser -filter * -Properties * -SearchBase "OU zum suchen" | ? {($_.surname -notlike "" -and $_.givenname -notlike "" -and ($_.physicalDeliveryOfficeName -like "Raum x" -or $_.physicalDeliveryOfficeName -like "Raum x") -and ($_.Description -notmatch "Description"))} | sort surname
        # Einbinden der benötigten Werte
    FOREACH($User in $Users){
        # Postfach auf das zugegriffen wird muss als Displayname eingetragen werden
        $User = $User | select -ExpandProperty displayname
        $mailbox = $outlook.Session.Folders.Item($User).Folders.Item($SENT).Items
        $mcount = $mailbox.Count
        FOR($i = $mcount ; $i -gt 0; $i--){
            $mail = $mailbox[$i]
        #foreach($mail in $mailbox){ --> Foreach hat einen Fehler beim Zähler verursacht
        # Herausfiltern von Kalendereinträgen. Nur Mails sollen gelöscht werden
            $mclass = $mail | select -ExpandProperty MessageClass
            IF($mclass -match "Schedule"){}
            ELSE{
        # laden der Werte Subject und Absender in je eine eigene Variable
                $subject = $mail | select -ExpandProperty Subject
                $sender = $mail | select -ExpandProperty SentOnBehalfOfName
        # Abfrage auf Bearbeitungscode und Absender
                IF($subject -match "MÄTCH" -and $sender -match "MÄTCH"){
        # Datum & Uhrzeit für logging
                    $dd = get-date -format dd.MM.yy
                    $dh = get-date -format hh:mm:ss
                    #$subject
                    $message = $dd+";"+$dh+";"+$User+";"+$subject
                    $log += $message
        # Löschen der gefilterten Mail fünf Durchläufe, da der erste nicht jede Mail erwischt
                        $mail.Delete()
                    Clear-Variable log, message, dd, dh
                    $log >> $logfilesentitems
                }
                ELSE{}
        # Löschen der im Verlauf eingesetzten Variablen - keep your ENV cleaned up
            Remove-Variable subject, sender 
            }
        Remove-Variable mail, mclass
        }
    Remove-Variable mailbox
    }
        # Schreiben des Endzeitpunkts ins log
    $ENDZEIT = get-date -format hh:mm:ss
    "$dateforlog"+";"+$ENDZEIT+";"+"Administrator"+";"+"Endzeitpunkt des Scripts" >> $logfilesentitems
        # Löschen aller eingesetzten Variablen - keep your ENV cleaned up
    Remove-Variable alle benutzten Variablen
        # Bye Bye
    EXIT

    Spart einiges an Ausführungszeit auf diese Weise.
    Frage mich dennoch weiterhin auch wie das bei einem FOREACH passieren kann, denn ich arbeite ja nicht mit der Indexnummer, sondern mit dem Betreff und wäre eigentlich davon ausgegangen, dass die Daten im Array so wie sie im Array stehen fortbestehen bleiben. Also wird doch vor dem Foreach eine eigene Liste aufgebaut und abgearbeitet ...




    • Bearbeitet BeatYa Freitag, 16. März 2018 11:01
    Freitag, 16. März 2018 10:44
  • Der ForEach baut keine eigene Liste auf, sondern verwendet eben die vorhandene Liste mit einem eigenen internen Zeiger.
    Er ist halt nur einfacher zu programmieren, als ein

    FOR($i = 0 ; $i -lt $mcount; $i++)

    Wenn du hier den Index N dann löscht, verschieben sich alle folgenden Einträge und wenn $mcount nicht neu gesetzt wird, kommt es sogar zu einem Zugriffsfehler.
    Man kann u.U. Glück haben, wenn man in der Schleife nach dem Entfernen, $i-- angibt, so dass beim Fortsetzen der Schleife mit $i++ nun der nachgerückte Eintrag (mit dem selben Index) bearbeitet wird.
    Allerdings könnte die interne Optimierung $i anders behandeln, so dass $i-- wirkungslos bleibt.
    In z.B. C#/C++ ist das durchaus der Fall.

    Wofür du allerdings das 5-fache Löschen noch brauchst, entzieht sich mir.

    Vielleicht hat ja $MailBox eine Remove/Delete-Methode für das Element.

    • Als Antwort markiert BeatYa Freitag, 16. März 2018 12:05
    • Tag als Antwort aufgehoben BeatYa Freitag, 16. März 2018 12:05
    Freitag, 16. März 2018 11:04
  • Der ForEach baut keine eigene Liste auf, sondern verwendet eben die vorhandene Liste mit einem eigenen internen Zeiger.
    Er ist halt nur einfacher zu programmieren, als ein

    FOR($i = 0 ; $i -lt $mcount; $i++)

    Wenn du hier den Index N dann löscht, verschieben sich alle folgenden Einträge und wenn $mcount nicht neu gesetzt wird, kommt es sogar zu einem Zugriffsfehler.
    Man kann u.U. Glück haben, wenn man in der Schleife nach dem Entfernen, $i-- angibt, so dass beim Fortsetzen der Schleife mit $i++ nun der nachgerückte Eintrag (mit dem selben Index) bearbeitet wird.
    Allerdings könnte die interne Optimierung $i anders behandeln, so dass $i-- wirkungslos bleibt.
    In z.B. C#/C++ ist das durchaus der Fall.

    Wofür du allerdings das 5-fache Löschen noch brauchst, entzieht sich mir.

    Vielleicht hat ja $MailBox eine Remove/Delete-Methode für das Element.

    Das 5-fache Löschen ist rausgeflogen, das hatte ich leider übersehen. Musste gleich mehrere Scripts umändern und das Foreach entziehen. Die anderen Scripts laufen fehlerfrei und nun auch rasend schnell durch. Das eine Script was ich hier als Beispiel angeführt habe ist auch das einzige nach Bearbeitung fehlerbehaftete Script gewesen .....^^

    Ich such mir Montag mal irgendeine passende Antwort als Antwort raus ;)

    • Bearbeitet BeatYa Freitag, 16. März 2018 13:29
    Freitag, 16. März 2018 12:08