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]
<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>
|
# 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. 
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. 
.
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

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”>
<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>
|
#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”>
<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!