Method invocation failed...doesn't contain a method named 'op_Addition'

Hi Folks,

I hope all is well. I have a .ps1 script that runs successfully. It uploads two files to an sftp site. I need to add another file that goes to a different directory in the same sftp site. I reviewed the code and “followed suit” by adding the vars and commands I thought that would be needed to include the third file transfer.

I get the following error when I run it:

Error: Method invocation failed because [WinSCP.TransferOperationResult] does not contain a method named ‘op_Addition’.

In my code below I made a comment above each new entry. You will see a “#”

Any help is appreciated.

try {
    #Load WinSCP .NET assembly
    Add-Type -Path "C:\Program Files (x86)\WinSCP\WinSCPnet.dll"

    #Set up session options
    $sessionOptions = New-Object WinSCP.SessionOptions -Property @{
        Protocol              = [WinSCP.Protocol]::Sftp
        HostName              = "x.x.x.x"
        UserName              = "UserName"
        Password              = "xxxxx"
        SshHostKeyFingerprint = "xxxxxxxxxxxxxxxxx"
    }

    $session = New-Object WinSCP.Session

    try
    {
        $currentDate = get-date -Format yyddMM
        $filePath = "\\source1 and 2\path\directory\"

        #added new source path
        $filePathOMS = "\\source3\path\directory\"

        $fileName1 = "OGBudgetD.csv"
        $fileName2 = "OGTrans.csv"

        #added new file
        $fileNameOMS = "OMS_KPI.csv"

        $file1 = $filePath + $fileName1
        $file2 = $filepath + $fileName2

        #new variable
        $fileOMS = $filePathOMS + $fileNameOMS

        #Connect
        $session.Open($sessionOptions)

        #Upload files
        $transferOptions = New-Object WinSCP.TransferOptions
        $transferOptions.TransferMode = [WinSCP.TransferMode]::Binary

        $transferResult = $session.PutFiles($file1, "/Budget/", $False, $transferOptions)
        $transferResult += $session.PutFiles($file2, "/Actuals/", $False, $transferOptions)

        #added new PutFile command
        $transferResult += $session.PutFiles($fileOMS, "/OMS_KPI/", $False, $transferOptions)

        $transferResult.Check()

        foreach ($transfer in $transferResult.Transfers)
        {
            #Added the new fileNameOMS
            Write-Host "Upload of $($transfer.fileName1 + "," + $transfer.fileName2 + "," + $transfer.fileNameOMS) succeeded."
        }
    }

    finally
    {
        $session.Dispose()
    }

    $EmailFrom = 'from@foo.com'
    $EmailTo = 'to@foo.com'
    $Subject = 'test Subj'
    $Body = "The Packages for OGOV SFTP ran successfully in Prod environment.
EmailReport variable: File sent: $FileName1 $FileName2 $fileNameOMS"
    $SMTPServer = 'serverName'

    Send-MailMessage -From $EmailFrom -To $EmailTo -Subject $Subject -Body $Body -SmtpServer $SMTPServer
}

catch
{
    Write-Host "Error: $($_.Exception.Message)"
}

Not all objects support addition, so you will have to explicitly type cast it as array of [WinSCP.TransferOperationResult]

# declare the variable as array
$transferOptions = @()

[quote quote=261494]Not all objects support addition, so you will have to explicitly type cast it as array of [WinSCP.TransferOperationResult]

PowerShell
<textarea class="urvanov-syntax-highlighter-plain print-no" style="tab-size: 4; font-size: 14px !important; line-height: 18px !important; z-index: 0; opacity: 0;" readonly="readonly" data-settings="dblclick"># declare the variable as array $transferOptions = @()</textarea>
1
2
# declare the variable as array
$transferOptions = @()
[/quote]

Thank you @kvprasoon for formatting it to look like code. I couldn’t figure out how to do it. :slight_smile:

Where do I declare the variable as an array? I tried to after line 39 and I got the same error.

As you can see, i’m not familiar w/ this.

#Upload files
$transferOptions = New-Object WinSCP.TransferOptions
$transferOptions = @()
$transferOptions.TransferMode = [WinSCP.TransferMode]::Binary
$transferResult = $session.PutFiles($file1, "/Budget/", $False, $transferOptions)

You have to declare variables before using it. If in a loop, it should be above the loop. Here, it can be anywhere above $transferOptions = New-Object WinSCP.TransferOptions

For code formatting, there is a detailed instruction https://powershell.org/forums/topic/guide-to-posting-code-2/

[quote quote=261533]You have to declare variables before using it. If in a loop, it should be above the loop. Here, it can be anywhere above $transferOptions = New-Object WinSCP.TransferOptions

For code formatting, there is a detailed instruction https://powershell.org/forums/topic/guide-to-posting-code-2/

[/quote]
I did what you recommended (see below) and I still get the same error. What else needs to be done?

#Upload files
$transferOptions = @()
$transferOptions = New-Object WinSCP.TransferOptions
$transferOptions.TransferMode = [WinSCP.TransferMode]::Binary
$transferResult = $session.PutFiles($file1, "/Budget/", $False, $transferOptions)
$transferResult += $session.PutFiles($file2, "/Actuals/", $False, $transferOptions)
$transferResult += $session.PutFiles($fileOMS, "/OMS_KPI/", $False, $transferOptions)

Ahh, my apologies, I mentioned the wrong variable. Here it should be $transferResult.

You should always add element to array once defined, so it will be like below.

$transferResult = @()
$transferOptions = New-Object WinSCP.TransferOptions
$transferOptions.TransferMode = [WinSCP.TransferMode]::Binary
$transferResult += $session.PutFiles($file1, "/Budget/", $False, $transferOptions)
$transferResult += $session.PutFiles($file2, "/Actuals/", $False, $transferOptions)
$transferResult += $session.PutFiles($fileOMS, "/OMS_KPI/", $False, $transferOptions)

[quote quote=261554]2
$transferResult = @()
$transferOptions = New-Object WinSCP.TransferOptions
$transferOptions.TransferMode = [WinSCP.TransferMode]::Binary
$transferResult += $session.PutFiles($file1, “/Budget/”, $False, $transferOptions)
$transferResult += $session.PutFiles($file2, “/Actuals/”, $False, $transferOptions)
$transferResult += $session.PutFiles($fileOMS, “/OMS_KPI/”, $False, $transferOptions)[/quote]

Wonderful! Thank you @kvprasoon. Can you provide any resource as to how this worked? (So I can learn from it)
thank you again for your patience and assistance. :slight_smile:

:+1:.

Arrays are basically collections and + operator is used to add elements to a collection. An undefined variable in PowerShell gets its type when its getting set for the first time. And the + operator is type depended, if the type of the variable is a string, then + does the append operation. If the variable is an integer, then + will do addition. Here the type WinSCP.TransferOperationResult is not a string and was trying to add it expecting it to be a collection, but not.

But in PowerShell arrays are heterogenous, means we can add any type of data in to a single array. Here its of the same WinSCP.TransferOperationResult type, since variable is defined as an array, here it adds value with WinSCP.TransferOperationResult type in all the three lines.

# Array with values of same type
$r = @(1,2,3)
$r += 4

$t = @('s','w','m')
$t += 'test'

# Array with values of different types
$rr = @(1,'2',{'ScriptBlock'})
$rr += Get-Process

+= kills puppies.

:smiley:
Do you think there would be a more elegant way to code something like this? i’m not too familiar w/ powershell fundamentals yet. i’ll get there some day.

@kvprasoon, thanks for your information. i’ll copy pasta into my powershell notes.

Arrays in PowerShell are of fixed in size. So doing += will actually create a new array with newly added element. hence its expensive with performance.

If working with large collections, its better to go with arraylist and use Add() method.

$r = New-Object -TypeName System.Collections.ArrayList
$r.Add(1)
$r.Add('string')
$r.Add((Get-Process))

I like the ‘bubble up’ approach, so you could define your file information in an object like a hashtable or psobject and use a loop. Results returned from PutFiles are rolled into or bubbled up in to $transferResult.

Note: This is psuedo-code and you can define these in appropriate places in your script:

#Use a hashtable to define file and path
$files = @{}
$files.Add("OGBudgetD.csv","/Budget/")
$files.Add("OGTrans.csv","/Actuals/")
$files.Add("OMS_KPI.csv","/OMS_KPI/")

$transferResult = foreach ( $file in $files.GetEnumerator() ) {
    $session.PutFiles($file.Key, $file.Value, $False, $transferOptions)
}

Using a PSObject gives you more flexibility as you could define all of the arguments that PutFiles requires. Note how for the OMS_KPI we specify that we DO want to remote the file, but do not for the previous two:

#Use a PSObject to define the file and path
$files = @"
LocalPath,RemotePath,Remove
"OGBudgetD.csv","/Budget/",$false
"OGTrans.csv","/Actuals/",$false
"OMS_KPI.csv","/OMS_KPI/",$true
"@ | ConvertFrom-Csv


$transferResult = foreach ( $file in $files ) {
    $session.PutFiles($file.LocalPath, $file.RemotePath, $file.Remove, $transferOptions)
}

[quote quote=262688]I like the ‘bubble up’ approach, so you could define your file information in an object like a hashtable or psobject and use a loop. Results returned from PutFiles are rolled into or bubbled up in to $transferResult.

Note: This is psuedo-code and you can define these in appropriate places in your script:

<link rel=“stylesheet” type=“text/css” href=“https://powershell.org/wp-content/plugins/urvanov-syntax-highlighter/themes/powershell-ise/powershell-ise.css”>
<link rel=“stylesheet” type=“text/css” href=“https://powershell.org/wp-content/plugins/urvanov-syntax-highlighter/fonts/liberation-mono.css”>

PowerShell
<textarea class="urvanov-syntax-highlighter-plain print-no" data-settings="dblclick" readonly="" style="tab-size: 4; font-size: 14px !important; line-height: 18px !important; z-index: 0; opacity: 0;">#Use a hashtable to define file and path $files = @{} $files.Add("OGBudgetD.csv","/Budget/") $files.Add("OGTrans.csv","/Actuals/") $files.Add("OMS_KPI.csv","/OMS_KPI/")

$transferResult = foreach ( $file in $files.GetEnumerator() ) {
$session.PutFiles($file.Key, $file.Value, $False, $transferOptions)
}</textarea>

1
2
3
4
5
6
7
8
9
#Use a hashtable to define file and path
$files = @{}
$files.Add("OGBudgetD.csv","/Budget/")
$files.Add("OGTrans.csv","/Actuals/")
$files.Add("OMS_KPI.csv","/OMS_KPI/")
$transferResult = foreach ( $file in $files.GetEnumerator() ) {
$session.PutFiles($file.Key, $file.Value, $False, $transferOptions)
}

Using a PSObject gives you more flexibility as you could define all of the arguments that PutFiles requires. Note how for the OMS_KPI we specify that we DO want to remote the file, but do not for the previous two:

<link rel=“stylesheet” type=“text/css” href=“https://powershell.org/wp-content/plugins/urvanov-syntax-highlighter/themes/powershell-ise/powershell-ise.css”>
<link rel=“stylesheet” type=“text/css” href=“https://powershell.org/wp-content/plugins/urvanov-syntax-highlighter/fonts/liberation-mono.css”>

PowerShell
<textarea class="urvanov-syntax-highlighter-plain print-no" data-settings="dblclick" readonly="" style="tab-size: 4; font-size: 14px !important; line-height: 18px !important; z-index: 0; opacity: 0;">#Use a PSObject to define the file and path $files = @" LocalPath,RemotePath,Remove "OGBudgetD.csv","/Budget/",$false "OGTrans.csv","/Actuals/",$false "OMS_KPI.csv","/OMS_KPI/",$true "@ | ConvertFrom-Csv

$transferResult = foreach ( $file in $files ) {
$session.PutFiles($file.LocalPath, $file.RemotePath, $file.Remove, $transferOptions)
}</textarea>

1
2
3
4
5
6
7
8
9
10
11
12
#Use a PSObject to define the file and path
$files = @"
LocalPath,RemotePath,Remove
"OGBudgetD.csv","/Budget/",$false
"OGTrans.csv","/Actuals/",$false
"OMS_KPI.csv","/OMS_KPI/",$true
"@ | ConvertFrom-Csv
$transferResult = foreach ( $file in $files ) {
$session.PutFiles($file.LocalPath, $file.RemotePath, $file.Remove, $transferOptions)
}
[/quote]

wooohhh, this is cool. i’m gonna experiment with it this way. thanks @Rob Simmons!