daabm 1.366 Geschrieben 16. November 2019 Melden Teilen Geschrieben 16. November 2019 GUI automatisieren mit Powershell ist schwierig. Du könntest einen Watcher bauen, der nach einer definierten Zeit den pdfinfo-Prozess abschießt, falls er zu lange aktiv ist. Zitieren Link zu diesem Kommentar
MurdocX 953 Geschrieben 19. November 2019 Melden Teilen Geschrieben 19. November 2019 Gute Idee. Das Umzusetzen wird aber nicht 0815 wie bisher. Stürzt das Programm denn ziemlich oft ab? Zitieren Link zu diesem Kommentar
Fosco 10 Geschrieben 19. November 2019 Autor Melden Teilen Geschrieben 19. November 2019 Leider so alle 5-8 Dateien. D.h. ich muss ständig dabei sitzen... Zitieren Link zu diesem Kommentar
BOfH_666 577 Geschrieben 19. November 2019 Melden Teilen Geschrieben 19. November 2019 (bearbeitet) ... n' bissl von hinten durch die Brust ins Auge, aber Du könntest den Aufruf des Tools in einen Job oder einen Runspace oder einfach in eine zweite, unabhängige Konsolen-Session auslagern und diese überwachen. Wenn das länger dauert als "erlaubt", schießt Du's ab und gehst davon aus, dass das Tool gecrashed ist oder das PDF korrupt ist oder beides. ooops ... steht ja schon da oben ... sorry, einfach ignorieren ... bearbeitet 19. November 2019 von BOfH_666 Zitieren Link zu diesem Kommentar
Fosco 10 Geschrieben 28. November 2019 Autor Melden Teilen Geschrieben 28. November 2019 Hi Leute, ich habe ein bisschen weiter versucht eine Lösung zu finden, Dabei bin ich auf folgende Variante gestoßen: foreach ($FileItem in $FoundPdfList) { $i++ Write-Progress -Activity 'Prüfe gefundene PDF-Dateien' -Status "$i von $($FoundPdfList.Count)" -CurrentOperation $FileItem.Name [string]$FilePath = $FileItem.FullName $retValue = & $PdfInfoToolPath $FilePath 2>&1 if ($retValue | Select-String -Pattern 'find trailer dictionary') { Move-Item -Path $FilePath -Destination $DefectFilesPath wait-process -name pdfinfo -timeout 5 stop-process -name pdfinfo -force } } Mit Outlook getestet, habe ich folgende Meldungen erhalten: 1. Outlook nicht beendet Wait-Process : Dieser Befehl hat den Vorgang beendet, da der Prozess "OUTLOOK (11624)" nicht innerhalb des angegebenen Timeouts beendet wurde. In C:\Users\alexander.thissen\Desktop\tools\test.ps1:1 Zeichen:1 + Wait-Process -Name OUTLOOK -Timeout 5 + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : CloseError: (System.Diagnostics.Process (OUTLOOK):Process) [Wait-Process], TimeoutException + FullyQualifiedErrorId : ProcessNotTerminated,Microsoft.PowerShell.Commands.WaitProcessCommand Stop-process hat Outlook dann auch einfach beendet. 2. Outlook in der Zeitspanne beendet Stop-Process : Es kann kein Prozess mit dem Namen "OUTLOOK" gefunden werden. Überprüfen Sie den Prozessnamen, und rufen Sie das Cmdlet erneut auf. In C:\Users\alexander.thissen\Desktop\tools\test.ps1:2 Zeichen:1 + Stop-Process -Name OUTLOOK -Force + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : ObjectNotFound: (OUTLOOK:String) [Stop-Process], ProcessCommandException + FullyQualifiedErrorId : NoProcessFoundForGivenName,Microsoft.PowerShell.Commands.StopProcessCommand Keine Meldung von wait-process, aber stop-process meldet, das der Prozess nicht gefunden wurde, Mein Gedanken gehen dahin, das die Funktion "stop-process", ähnlich wie das verschieben der Dateien, nur ausgeführt wird, wenn "wait-process" die ErrorId "ProcessNotTerminated" ausgibt. Müsste dann, denke ich, eine If-Schleife innerhalb der If-Schleife zum umkopieren sein. Was meint Ihr? Könnte das so funktionieren, und wenn ja, wie setzt man das um (evtl. auch so, das die Meldungen zu wait und stop nicht dargestellt werden)? Nochmals Danke..... Zitieren Link zu diesem Kommentar
daabm 1.366 Geschrieben 28. November 2019 Melden Teilen Geschrieben 28. November 2019 -Erroraction und Try/Catch lösen das. Und Wait-Process hatte ich gar nicht auf dem Schirm - das ist in Verbindung mit Start-Process sicher ein guter Ansatz. Zitieren Link zu diesem Kommentar
Fosco 10 Geschrieben 29. November 2019 Autor Melden Teilen Geschrieben 29. November 2019 Danke für den Tip. Ich bin jetzt bei diesem Stand: foreach ($FileItem in $FoundPdfList) { $i++ Write-Progress -Activity 'Prüfe gefundene PDF-Dateien' -Status "$i von $($FoundPdfList.Count)" -CurrentOperation $FileItem.Name [string]$FilePath = $FileItem.FullName $retValue = & $PdfInfoToolPath $FilePath 2>&1 $retValueProcess = ??? if ($retValue | Select-String -Pattern 'find trailer dictionary') { Move-Item -Path $FilePath -Destination $DefectFilesPath wait-process -name pdfinfo -timeout 5 -ErrorAction SilentlyContinue if ($retValueProcess | Select-String -Pattern 'ProcessNotTerminated') { stop-process -name pdfinfo -force -ErrorAction SilentlyContinue } } } Was mir noch nicht klar ist, wäre die Übernahme des Fehlercodes von wait-process in die Variable "$retValueProcess". Hier wird ja auf eine Rückmeldung eines Standartbefehls Bezug genommen. Im Gegensatz dazu wird bei "$retValue" die Variable aus einem Vorgegeben Pfad und einen ausgelesenen Pfad zusammen gesetzt. Auch wenn ich zugeben muss, das mir da auch die Syntax nicht klar ist... Danke für die Hilfe... Zitieren Link zu diesem Kommentar
daabm 1.366 Geschrieben 29. November 2019 Melden Teilen Geschrieben 29. November 2019 So geht das nicht. Wenn Du PDFInfo asynchron startest, kommst Du nicht "direkt" an einen Returncode ran. Und so wie Dein Beispielcode oben aussieht, ist das wieder nicht asynchron und hängt dann halt, wenn $PDFInfoToolPath nicht zurückkommt... Multithreading ist nicht einfach am Anfang Zitieren Link zu diesem Kommentar
speer 19 Geschrieben 30. November 2019 Melden Teilen Geschrieben 30. November 2019 Wieso verwendest du nicht einfach Start-process? Da genügt die foreach Schleife und start-process plus etwas logging aus. Außerdem kann mittels -wait eine "sequentielle" Abarbeitung durchgeführt werden. Somit läuft wahrscheinlich auch Dein PDF Programm stabiler. Link zu Microsoft - Start-Process Zitieren Link zu diesem Kommentar
daabm 1.366 Geschrieben 1. Dezember 2019 Melden Teilen Geschrieben 1. Dezember 2019 Mit Start-Process -wait bist Du nicht mehr asynchron. Hängt Dein gestarteter Prozess, hängst Du selbst auch Zitieren Link zu diesem Kommentar
speer 19 Geschrieben 2. Dezember 2019 Melden Teilen Geschrieben 2. Dezember 2019 Ich würde es nicht asynchron machen. Beim obigen Programm hat das meiner Meinung nach nur Nachteile. Erstens muss die Prozess-ID für jeden gestarteten Prozess gesichert werden. Zweitens muss permanent geprüft werden ob die Prozesse beendet und falls ja, mit welchem Status diese beendet wurden. Glaube bei solch einem Programm kommt es nicht auf Multithreading an. Da eignet sich C# besser als Powershell. Zitieren Link zu diesem Kommentar
Fosco 10 Geschrieben 3. Dezember 2019 Autor Melden Teilen Geschrieben 3. Dezember 2019 Danke für eure Antworten.... Aber ich geb zu, jetzt bin ich komplett raus und kann euch nicht mehr folgen... Zitieren Link zu diesem Kommentar
BOfH_666 577 Geschrieben 4. Dezember 2019 Melden Teilen Geschrieben 4. Dezember 2019 (bearbeitet) So ... jetzt ich nochmal. Ich hab nochmal gründlich über die Aufgabenstellung und die von Dir geschilderten Probleme nachgedacht und hab versucht, das Ganze in kleinere Teile zu zerlegen. Da das PDFInfo-Tool bei Dir offenbar ziemlich instabil läuft - ich kann das auf meinem Computer nicht nachstellen - bräuchtest Du wohl als erstes eine Funktion, die das Tool starten kann, deren Ausführung überwachen und, wenn sich das Tool dann eben doch aufgehängt hat, das Tool beendet und Dir die Möglichkeit bietet, diesen Status zu erkennen und trotzdem weiterzumachen. Danach musst Du die Ausgabe des Tools auswerten. Wenn die Ausgabe einen entsprechenden Fehler enthält, wird die gerade verarbeitete Datei ignoriert. Wenn die Ausgabe die erwartete Information enthält, wird die Datei kopiert. Also hier meine Idee zur Funktion, die das Tool in einem Hintergrund-Job startet und dann eine einstellbare Zeit wartet, dass das Tool sich von selbst beendet und eine Ausgabe liefert: function Start-ProcessWithTimeout { [CmdletBinding()] [OutputType([System.String])] param( [Parameter(Position = 0, Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $FilePath, [Parameter(Position = 1)] [ValidateNotNull()] [System.String[]] $ArgumentList, [Parameter(Position = 2)] [ValidateNotNull()] [System.Int32] $Timeout = 10 ) $SBFilePath = $FilePath $SBArgumentList = $ArgumentList $ScriptBlock = { param( $SBFilePath, $SBArgumentList ) $TmpStdOut = New-TemporaryFile $TmpStdErr = New-TemporaryFile Start-Process -FilePath $SBFilePath -ArgumentList $SBArgumentList -NoNewWindow -RedirectStandardError $TmpStdErr -RedirectStandardOutput $TmpStdOut -Wait $StdErr = Get-Content -Path $TmpStdErr.FullName $StdOut = Get-Content -Path $TmpStdOut.FullName Remove-Item -Path $TmpStdErr -Force Remove-Item -Path $TmpStdOut -Force $StdErr $StdOut } $JobProps = @{ Name = 'BackGroundJob' ScriptBlock = $ScriptBlock } [VOID](Start-Job @JobProps -ArgumentList $SBFilePath, $SBArgumentList ) $StartTime = Get-Date do { $JobState = Get-Job -Name BackGroundJob Write-Debug "JobState: $($JobState.State) - HasMOreData: $($JobState.HasMoreData)" if ($JobState.State -eq 'Completed') { $HasMoreData = $JobState.HasMoreData if ($JobState.HasMoreData) { $Result = Receive-Job -Name BackGroundJob Remove-Job -Name BackGroundJob break } } else { Start-Sleep -Milliseconds 50 } } while ((Get-Date) -lt $StartTime.AddSeconds($Timeout)) if (Get-Job -Name BackGroundJob -ErrorAction SilentlyContinue) { Stop-Job -Name BackGroundJob Remove-Job -Name BackGroundJob -Force } [PSCustomObject]@{ State = $JobState.State HasMoreData = $HasMoreData Runtime = New-TimeSpan -Start $JobState.PSBeginTime -End $JobState.PSEndTime Result = $Result } } Leider gibt es noch ein Problem, wenn die Datei oder der Pfad ein Leerzeichen enthält. Das hab ich noch nicht lösen können. Wenn Du solche Datei hast, könntest Du die ja vielleicht vorher entsprechend umbenennen ... z.B. Leerzeichen durch Unterstriche ersetzen oder so. Oder Du ersetzt die Leerzeichen temporär durch ein einigermaßen exotisches Zeichen. Dann kannst Du sie später wieder relativ einfach zurück-umbenennen. In der Voreinstellung wartet die Funktion 10 Sekunden darauf, dass das Tool sich wieder beendet. Das war für meine Tests mehr als genug. Wenn das bei Dir anders ist, kannst Du den Timeout beim Aufrufen der Funktion entsprechend anpassen. Jetzt könntest Du diese Funktion ungefähr so benutzen: $ToolPath = '<kompletter Pfad zum PDFInfo-Tool>\bin64\pdfinfo.exe' Get-ChildItem -Path <Pfad zu den zu verarbeitenden PDF-Dateien> -Filter *.pdf | ForEach-Object{ $PDFFilePath = $PSItem.FullName # hier wird der komplette Pfad zur PDF-Datei innerhalb der switch-Anweisung nutzbar gemacht $Result = Start-ProcessWithTimeout -FilePath $ToolPath -ArgumentList $PSItem.FullName # -Timeout 20 # anpassen falls nötig If($Result.State -eq 'Completed' -and $Result.HasMoreData){ switch ($Result.Result) { {$Result.Result -match 'Usage:\s+pdfinfo'} { "PDF-Tool SyntaxError - $PDFFilePath"; break } {$Result.Result -match 'PDF\s+file\s+is\s+damaged'} { "PDF file is damaged - $PDFFilePath"; break } {$Result.Result -match 'PDF\s+version:'} { "PDF file is valid - $PDFFilePath"; break } } } Else{ "PDF-Tool stuck ... - $PDFFilePath" } } Dieses Beispiel gibt nur Strings auf der Console aus. Die eigentlichen Aktionen musst Du dann entsprechend Deiner Anforderungen einbauen. Natürlich kann man den switch block noch beliebig erweitern, z.B. je nach Author die PDFs in verschiedene Ordner verschieben oder, oder , oder ... Ich hoffe, es hilft. (... und es funktioniert wie erhofft ... wie gesagt, bei mir stürzt das Tool leider nicht ab) bearbeitet 4. Dezember 2019 von BOfH_666 Zitieren Link zu diesem Kommentar
Fosco 10 Geschrieben 4. Dezember 2019 Autor Melden Teilen Geschrieben 4. Dezember 2019 Vielen Dank für deine Mühen... Den unteren Teil habe ich verstanden (denke ich) und mal so angepasst, wie ich es mir (als Laie) denke. Problem ist, das auch eine zu öffnende Datei als defekt erkannt werden kann. Daher habe ich den String von "PDF file is damaged" in (Could´t) "find Trailer Dictionary" abgeändert. Damit sollten nur noch wirklich defekte Dateien verschoben werden. $ToolPath = '<kompletter Pfad zum PDFInfo-Tool>\bin64\pdfinfo.exe' $DefectFilesPath = 'Zielordner' Get-ChildItem -Path <Pfad zu den zu verarbeitenden PDF-Dateien> -Filter *.pdf | ForEach-Object{ $PDFFilePath = $PSItem.FullName # hier wird der komplette Pfad zur PDF-Datei innerhalb der switch-Anweisung nutzbar gemacht $Result = Start-ProcessWithTimeout -FilePath $ToolPath -ArgumentList $PSItem.FullName # -Timeout 20 # anpassen falls nötig If($Result.State -eq 'Completed' -and $Result.HasMoreData){ switch ($Result.Result) { {$Result.Result -match 'Usage:\s+pdfinfo'} { "PDF-Tool SyntaxError - $PDFFilePath"; break } {$Result.Result -match 'find\s+trailer\s+dictionary'} { Move-Item -Path $PDFFilePath -Destination $DefectFilesPath; break } {$Result.Result -match 'PDF\s+version:'} { "PDF file is valid - $PDFFilePath"; break } } } Else{ "PDF-Tool stuck ... - $PDFFilePath" } } Unsicher bin ich mit dem $PDFFilePath im switch-block. Da es sich um eine Datei handelt würde ich gefühlsmäßig eher zu $PSItem.Fullname tendieren. Aber da kann ich natürlich auch "heavy on the woodway" sein... Ich habe es auch so verstanden, das die Funktion die du erstellt hast innerhalb der ForEach-Schleife gestartet wird (Start-ProcesswithTimeout). Was mir noch unklar ist, wie gehe ich mit der Funktion um? Wo und/oder Wie wird sie hinterlegt? Muss die irgendwo eingefügt werden? Danke Fosco Zitieren Link zu diesem Kommentar
BOfH_666 577 Geschrieben 4. Dezember 2019 Melden Teilen Geschrieben 4. Dezember 2019 vor 17 Minuten schrieb Fosco: Problem ist, das auch eine zu öffnende Datei als defekt erkannt werden kann. Daher habe ich den String von "PDF file is damaged" in (Could´t) "find Trailer Dictionary" abgeändert. Damit sollten nur noch wirklich defekte Dateien verschoben werden. Ja ... das solltest Du für Dich anpassen, wie es am besten passt. Ich hatte nur einen sehr kleinen Pool an PDF-Dateien zum Testen und hatte mir nur 2 defekte PDFs gebastelt. vor 17 Minuten schrieb Fosco: Unsicher bin ich mit dem $PDFFilePath im switch-block. Da es sich um eine Datei handelt würde ich gefühlsmäßig eher zu $PSItem.Fullname tendieren. Aber da kann ich natürlich auch "heavy on the woodway" sein... Ich kann's im Moment nicht verifizieren, aber innerhalb des switch - Blocks verweist $PSItem auf das zu testende "switch-Element" ... deshalb die extra Variable. vor 17 Minuten schrieb Fosco: Was mir noch unklar ist, wie gehe ich mit der Funktion um? Wo und/oder Wie wird sie hinterlegt? Muss die irgendwo eingefügt werden? Das kannst Du machen, wie Du möchtest. Die Funktion muss eben spätestens wenn Du sie aufrufen möchtest definiert sein. Du könntest sie in einer extra Datei speichern, die Du am Anfang Deines Scriptes mittels Dot-Sourcing einbindest. Oder Du packst die Funktion direkt mit in die Script-Datei. Oder Du packst sie mit ins Profil des Users oder PCs, wo das Script ausgeführt werden soll. Oder, oder, oder ... Zitieren Link zu diesem Kommentar
Empfohlene Beiträge
Schreibe einen Kommentar
Du kannst jetzt antworten und Dich später registrieren. Falls Du bereits ein Mitglied bist, logge Dich jetzt ein.