Verlassen einer UDF mit Return funktioniert nicht

  • Hallo zusammen

    Stehe vor einem Rätsel; habe eine Func erstellt und möchte diese an einer bestimmten Zeile mit "Return" wieder verlassen und dabei auch einen Wert zurückgeben. Klappte an jeder anderen Stelle bisher wunderbar, jetzt plötzlich nicht mehr.

    Folgendes Skript soll das Laufwerk C: nach .exe-Files durchsuchen und von jedem einzelnen File den Pfad zurückgeben.

    [autoit]


    $Path = Search(".exe","C:")
    MsgBox(0,"$Path",$Path)

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

    Func Search($app,$Dir) ;ohne GUI
    Local $Search
    Local $File
    Local $FileAttributes
    Local $FullFilePath

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

    $Search = FileFindFirstFile($Dir & "\*.*")

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

    While 1
    If $Search = -1 Then
    ExitLoop
    EndIf

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

    $File = FileFindNextFile($Search)
    If @error Then ExitLoop

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

    $FullFilePath = $Dir & "" & $File
    $FileAttributes = FileGetAttrib($FullFilePath)

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

    If StringInStr($FileAttributes,"D") Then ;ordner gefunden
    Search($app,$FullFilePath)
    Else ;datei gefunden
    if StringRight($FullFilePath,4) = ".exe" Then
    if StringInStr($FullFilePath, $app,2) <> 0 Then
    $use = MsgBox(3,"Objekt gefunden","Soll folgendes Objekt verwendet werden?"&@LF&$FullFilePath)
    Select
    case $use = "6"
    MsgBox(0,"OK",$FullFilePath)
    Return $FullFilePath
    case $use = "2"
    MsgBox(0,"Abbruch","Suche durch den Benutzer abgebrochen")
    exit
    EndSelect
    EndIf
    EndIf
    EndIf
    WEnd
    FileClose($Search)
    EndFunc

    [/autoit]

    Die Files werden auch gefunden und die MsgBox in Zeile 31 wird auch ausgeführt. Nur das Verlassen der Funktion in Zeile 32 funktioniert nicht. Es ist nicht so, dass nichts zurück gegeben wird, sondern die While-Schleife läuft einfach weiter, obwohl die ganze Funktion verlassen werden sollte.

    Weiss jemand Rat? Danke für euere Hilfe!

    Einmal editiert, zuletzt von point-man (12. Mai 2014 um 20:28)

  • Naja wird sehr sicher an deiner Rekursion liegen. Sollte ein Ordner anstatt einer Datei gefunden werden ruft sich die Funktion selbst auf, ab hier hast du mindestens 2 verschachtelte Instanzen der Funktion laufen, ein Return der inneren Intanz springt wieder zurück zur nächst äußeren und nicht wie von dir erhofft zurück in den globalen Kontext deines Scriptes. Es erscheint also mindesten ein zweiter Treffer. Das ganze wird noch weiter verschachtelt wenn mehrere Unterverzeichnisse vorhanden sind. Wenn du also wirklich nach dem ersten "zu nutzenden" Treffer abbrechen willst empfhielt sich ein globales Flag, dass direkt nach dem rekursiven Aufruf zu einem Return führt.

    [autoit]


    global $abort = False
    $Path = Search(".exe","C:")
    MsgBox(0,"$Path",$Path)

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

    Func Search($app,$Dir) ;ohne GUI
    ;...
    If StringInStr($FileAttributes,"D") Then ;ordner gefunden
    $ret = Search($app,$FullFilePath)
    if $abort = True then return $ret ; reicht den Return Wert der inneren Rekursionsstufe nach außen weiter
    Else ;datei gefunden
    if StringRight($FullFilePath,4) = ".exe" Then
    if StringInStr($FullFilePath, $app,2) <> 0 Then
    $use = MsgBox(3,"Objekt gefunden","Soll folgendes Objekt verwendet werden?"&@LF&$FullFilePath)
    Select
    case $use = "6"
    MsgBox(0,"OK",$FullFilePath)
    $abort = True
    Return $FullFilePath
    case $use = "2"
    MsgBox(0,"Abbruch","Suche durch den Benutzer abgebrochen")
    exit
    EndSelect
    ;...

    [/autoit]

    EDIT: Ahja und das Searchhandle sollte natürlich auch in jeder Rekursionsstufe geschlossen werden bevor du return verwendest, ansonsten hast du haufenweise handles die du nicht mehr nachträglich schließen kannst.

    Einmal editiert, zuletzt von misterspeed (11. Mai 2014 um 09:29)

    • Offizieller Beitrag

    Ich würde @error anstatt einer globalen Variablen benutzen. Außerdem muss man nicht mehr FileGetAttrib benutzen, um die Unterscheidung "Datei/Ordner" zu treffen. Das Makro @extended wird bei einem Ordner gesetzt.
    Das ganze würde dann so aussehen:

    Spoiler anzeigen
    [autoit]


    $Path = Search(".exe", "C:")
    MsgBox(0, "$Path", $Path)

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

    Func Search($app, $Dir) ;ohne GUI
    Local $Search, $File, $FullFilePath
    $Search = FileFindFirstFile($Dir & '\*.*')
    While 1
    If $Search = -1 Then ExitLoop
    $File = FileFindNextFile($Search)
    If @error Then ExitLoop
    $FullFilePath = $Dir & '\' & $File
    If @extended Then ;ordner gefunden
    $FullFilePath = Search($app, $FullFilePath)
    If @error Then
    FileClose($Search)
    Return SetError(1, 0, $FullFilePath)
    EndIf
    Else ;datei gefunden
    If StringRight($FullFilePath, 4) = ".exe" Then
    If StringInStr($FullFilePath, $app, 2) Then
    Switch MsgBox(3, "Objekt gefunden", "Soll folgendes Objekt verwendet werden?" & @LF & $FullFilePath)
    Case "6"
    Return SetError(1, 0, $FullFilePath)
    Case "2"
    MsgBox(0, "Abbruch", "Suche durch den Benutzer abgebrochen")
    Return SetError(2, 0, '')
    EndSwitch
    EndIf
    EndIf
    EndIf
    WEnd
    FileClose($Search)
    Return SetError(0, 0, $FullFilePath)
    EndFunc ;==>Search

    [/autoit]
  • Woa, ihr seit super! Da wäre ich im Leben nicht drauf gekommen. Danke vielmals! Werd ich gleich mal ausprobieren und versuchen zu verstehen ;) Danke!

    Die Problemlösung an und für sich verstehe ich, zumindest prinzipiell. Aber wie funktioniert das mit der @extended-Abfrage genau? Wie/wo/wann wird der Wert für einen Ordner gesetzt? Werde diesbezüglich nicht schlau aus der Dokumentation.

    Einmal editiert, zuletzt von point-man (12. Mai 2014 um 20:23)