Search code examples
powershellftpwinscppowershell-5.0winscp-net

How can I remove a FileTransferred event handler from a WinSCP Session object?


I made a small PowerShell wrapping script for the WinSCP module that suites my needs. Like in the example for synchronizing local and remote folders mine uses a file transferred event to log session events.

$fileTransferedEvent = {Receive-FileTransferredEvent $_}
$session.add_FileTransferred($fileTransferedEvent)

The example disposes the session when it is done. My logic keeps the session open until I choose to be done with it so I can use the same session in other cmdlet call.

This creates an issue if a other cmdlets (or even calling the same one again) also use add_FileTransferred(). The event gets added again which triggers X times the output where X is how many times the method is called.

I found a partner method called remove_FileTransferred() but I do not know how to use it

Name                   MemberType Definition                                                           
----                   ---------- ----------                                                           
add_FileTransferred    Method     void add_FileTransferred(WinSCP.FileTransferredEventHandler value)   
remove_FileTransferred Method     void remove_FileTransferred(WinSCP.FileTransferredEventHandler value)

They both accept the same values so I thought maybe I could pass it a definition of the the handler but that does not seem to work i.e. $session.remove_FileTransferred($fileTransferedEvent) did not do the job. Nor does it error either.

How can I remove file transferred event handlers on Winscp.Session objects?


Solution

  • This works for me perfectly:

    $fileTransferedEvent = { Write-Host "Upload of $($_.FileName) succeeded" }
    $session.add_FileTransferred($fileTransferedEvent)
    $session.PutFiles("C:\test.txt", "/").Check()
    $session.remove_FileTransferred($fileTransferedEvent)
    $session.PutFiles("C:\test.txt", "/").Check()
    

    I get:

    Upload of C:\test.txt succeeded
    

    While if I do not call remove_FileTransferred, I get the output twice, as expected:

    Upload of C:\test.txt succeeded
    Upload of C:\test.txt succeeded
    

    Refer to Event handlers in PowerShell.


    If you add the same event handler multiple times, the handler will be called that many times each time the event is raised. If you want to cancel an event subscription, you have to remove the handler the same number of time. Removing handler, which is not (or no longer) added is noop. All these are general rules, not limited to Session.FileTransferred or WinSCP .NET assembly.

    Example:

    $fileTransferedEvent = { Write-Host "Upload of $($_.FileName) succeeded" }
    $session.add_FileTransferred($fileTransferedEvent)
    $session.add_FileTransferred($fileTransferedEvent)
    
    Write-Host "two handlers:"
    $session.PutFiles("C:\test.txt", "/").Check()
    
    $session.remove_FileTransferred($fileTransferedEvent)
    Write-Host "one handler:"
    $session.PutFiles("C:\test.txt", "/").Check()
    
    $session.remove_FileTransferred($fileTransferedEvent)
    Write-Host "zero handlers:"
    $session.PutFiles("C:\test.txt", "/").Check()
    
    Write-Host "done"
    

    This will get you:

    two handlers:
    Upload of C:\test.txt succeeded
    Upload of C:\test.txt succeeded
    one handler:
    Upload of C:\test.txt succeeded
    zero handlers:
    done
    

    Though, what may not be obvious is, that the following two code snippets are not identical:

    $fileTransferedEvent = { Write-Host "Upload of $($_.FileName) succeeded" }
    $session.add_FileTransferred($fileTransferedEvent)
    $fileTransferedEvent = { Write-Host "Upload of $($_.FileName) succeeded" }
    $session.add_FileTransferred($fileTransferedEvent)
    
    $fileTransferedEvent = { Write-Host "Upload of $($_.FileName) succeeded" }
    $session.add_FileTransferred($fileTransferedEvent)
    $session.add_FileTransferred($fileTransferedEvent)
    

    The first snippet adds two distinct event handlers, while the latter adds two identical handlers.

    After the first snippet, you cannot remove the two registrations by calling this:

    $session.remove_FileTransferred($fileTransferedEvent)
    $session.remove_FileTransferred($fileTransferedEvent)
    

    The first "remove" will remove the last/second added handler. The second "remove" will do nothing, as it tries to remove the same handler, which was already cancelled. As you do not have any reference to the first handler, you cannot cancel it anymore.