SFTP using WinSCP dotNet

Started by Deana, October 31, 2013, 10:10:34 AM

Previous topic - Next topic

Deana

The WinBatch Extenders: Winsock and WinInet do not currently support SFTP.  However WinSCP ( http://winscp.net/eng/docs/introduction ) is an open source freeware SFTP client for Windows using SSH. Its main function is safe copying of files between a local and a remote computer. It supports scripting and command-line interface.

Using dotNet with WinSCP: http://winscp.net/eng/docs/guide_dotnet

I  put together a WinBatch dotNet code sample that uses WinSCP to handle SFTP.

Quote
REQUIRES WinBatch 2013A or newer
Before starting you should:
- Download and install both WinSCP and its .NET assembly/COM library (http://winscp.net/eng/download.php)
- Know how to connect to your FTP/SFTP account;

Here is the code. You will need to modify for your environment...

Code (winbatch) Select

;***************************************************************************
;**   SFTP using WinSCP dotNet
;**
;** Purpose:  SFTP using WinSCP dotNet
;** Inputs:
;** Outputs:
;** Reference:
;**       REQUIRES WinBatch 2013A or newer
;**       http://winscp.net/eng/docs/guide_dotnet
;**       http://winscp.net/eng/docs/library (WinSCP .NET Assembly and COM Library)
;**       Before starting you should:
;**       - Download and install both WinSCP and its .NET assembly/COM library (http://winscp.net/eng/download.php)
;**         By default WinScp installs to C:\Program Files (x86)\WinSCP you will need to copy the WinSCp.dll to this directory
;**       - Know how to connect to your FTP/SFTP account;
;**       - Know what WinSCP scripting commands to use for your task (e.g. file transfer).
;**       
;** Developer: Deana Falk 2013.10.31
;***************************************************************************
If Version( )< '2013A'
   Pause( 'Notice', 'Need 2013A or Newer Version of WinBatch' )
   Exit
EndIf

assemblydir = 'C:\Program Files (x86)\WinSCP'
ObjectClrOption( 'appbase', assemblydir )
ObjectClrOption ( 'use', 'WinSCP, Version=1.0.7.3446, Culture=neutral' )  ; Confirm version matches the dll

;Create an instance of the WinSCP.SessionOptions class and fill in all necessary information to allow an automatic connection and authentication of your session.
objSessionOptions = ObjectClrNew( 'WinSCP.SessionOptions' )

; Define Protocol
;  Possible values are Protocol.Sftp (default), Protocol.Scp and Protocol.Ftp.
objProtocol = ObjectClrNew( 'WinSCP.Protocol' ) 
Sftp = ObjectClrType( 'WinSCP.Protocol', objProtocol.Sftp ) ; Sftp
;Ftp = ObjectClrType( 'WinSCP.Protocol', objProtocol.Ftp ) ; Ftp          ; MODIFY TO FIT YOUR NEEDS
;Scp = ObjectClrType( 'WinSCP.Protocol', objProtocol.Scp ) ; Scp   

objSessionOptions.Protocol = Sftp  ; Sftp, Ftp or Scp                    ; MODIFY TO FIT YOUR NEEDS
objSessionOptions.HostName = 'ftp.example.com'                           ; MODIFY TO FIT YOUR NEEDS
objSessionOptions.UserName = 'user'                                      ; MODIFY TO FIT YOUR NEEDS
objSessionOptions.Password = 'password'                                  ; MODIFY TO FIT YOUR NEEDS
If IsDefined(Sftp)
   objSessionOptions.SshHostKeyFingerprint = "ssh-rsa 1024 xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx" ;REQUIRED by SFTP     ; MODIFY TO FIT YOUR NEEDS
Endif


;Create an instance of the WinSCP.Session class.
objSession = ObjectClrNew("WinSCP.Session" )

;.Open the session using Session.Open method, passing instance of your WinSCP.SessionOptions.
objSession.Open(objSessionOptions)


;Once the session is opened, you can use any of the WinSCP.Session methods to manipulate remote files, e.g.,
;objSession.GetFiles to download files,
;objSession.PutFiles to upload files or
;objSession.SynchronizeDirectories to synchronize directories.

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; GetFiles sample
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; Force binary mode transfer
objTransferOptions = ObjectClrNew( 'WinSCP.TransferOptions' )
objTransferMode = ObjectClrNew( 'WinSCP.TransferMode' )   
objTransferOptions.TransferMode = ObjectClrType( 'WinSCP.TransferMode', objTransferMode.Binary ) 
; Download file to the local directory - Note use of absolute path
bFalse = ObjectType( 'BOOL', 0 )
transferResult = objSession.GetFiles( '/wwwftp/2013/Readme.txt', 'c:\temp\', bFalse, objTransferOptions)       ; MODIFY TO FIT YOUR NEEDS


; Disconnect, clean up
objSession.Dispose()
objTransferOptions = 0
objSession = 0
objProtocol = 0
objSessionOptions = 0
Exit
Deana F.
Technical Support
Wilson WindowWare Inc.

td

A few choice search terms in the search engine of your choice should give you a link to a page with a downloadable, free to try, SFTP server you can install right on your desktop for testing purposes.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

Deana

Quote from: td on October 31, 2013, 12:57:48 PM
A few choice search terms in the search engine of your choice should give you a link to a page with a downloadable, free to try, SFTP server you can install right on your desktop for testing purposes.

Thanks Tony. I quickly set up a FileZilla FTP server to test the code. Looks good. I am posting it to the tech database here: http://techsupt.winbatch.com/webcgi/webbatch.exe?techsupt/tsleft.web+WinBatch/dotNet/Third~Party~Components/WinScp+SFTP~using~WinSCP~dotNet~.txt

We have had a few users request SFTP in WinBatch. Hopefully this will help get them started.
Deana F.
Technical Support
Wilson WindowWare Inc.

kdmoyers

THIS is some seriously useful code.  thanks so much Deana.

And thanks for all the other great examples you've been posting recently.  Great stuff.

-Kirby
The mind is everything; What you think, you become.

Deana

Quote from: kdmoyers on November 01, 2013, 11:04:19 AM
THIS is some seriously useful code.  thanks so much Deana.

And thanks for all the other great examples you've been posting recently.  Great stuff.

-Kirby

Happy to hear you will find this useful!
Deana F.
Technical Support
Wilson WindowWare Inc.

warren

Hi Deana,

I am converting our winbatch apps to SFTP and am happy to see this method. In the past I was making calls to the DOS Putty psftp app and that works but this should be better.

The issue I am having is error trapping. Unfortunately I am a .Net neophyte so this is hurting my brain.

When the server doesn't exist or the login fails the objSession.Open(objSessionOptions) throws an error. I can stop it from doing that by turning off the errors, but I can't figure out how to trap the error. objSession.SessionRemoteException should show the error but I can't figure out how to call it. Sample of one way that didn't work below. Can you help?

ErrorMode(@OFF)
objSession.Open(objSessionOptions)
ErrorMode(@CANCEL)
currError = ObjectClrNew( 'objSession.SessionRemoteException')

message ("!", currError)

Thanks!

Deana

Quote from: warren on March 19, 2014, 12:11:11 PM
Hi Deana,

I am converting our winbatch apps to SFTP and am happy to see this method. In the past I was making calls to the DOS Putty psftp app and that works but this should be better.

The issue I am having is error trapping. Unfortunately I am a .Net neophyte so this is hurting my brain.

When the server doesn't exist or the login fails the objSession.Open(objSessionOptions) throws an error. I can stop it from doing that by turning off the errors, but I can't figure out how to trap the error. objSession.SessionRemoteException should show the error but I can't figure out how to call it. Sample of one way that didn't work below. Can you help?

ErrorMode(@OFF)
objSession.Open(objSessionOptions)
ErrorMode(@CANCEL)
currError = ObjectClrNew( 'objSession.SessionRemoteException')

message ("!", currError)

Thanks!


You can trap errors in WinBatch using IntControl 73. The "additional error information" (in the case of CLR the exception) can be obtained using the wberroradditionalinfo variable defined when an error occurs. For more about error handling in WinBatch please read:  http://techsupt.winbatch.com/webcgi/webbatch.exe?techsupt/tsleft.web+Tutorials+Trap~Errors.txt

Note: You will get the same information when it errors in WinBatch. Including the "extended error information":


[COM/CLR Exception]
mscorlib=Method 'System.Net.Mail.SmtpClient.Port' not found.
[COM & CLR Sub-system]
Function=InvokeMember
ErrorCode=5394 (0x80131512)





Simply insert following line to the beginning of your script 

Code (winbatch) Select
IntControl(73,1,0,0,0); Initiate error handling

Then place this code after your Exit statement

Code (winbatch) Select
:WBERRORHANDLER
lasterr = wberrorarray[0]
handlerline = wberrorarray[1]
textstring = wberrorarray[5]
exterror =  wberrorarray[6]
linenumber = wberrorarray[8]
errstr = StrCat("Number: ",lasterr,@LF,"String: ",textstring,@LF,"Extended Error: ",exterror,@LF,"Line (",linenumber,"): '",handlerline,"'")
Message("Error Information",errstr)
Exit





Deana F.
Technical Support
Wilson WindowWare Inc.

warren

Thanks for the code it will be useful in the future, but it's not exactly what I was looking for. It just shows things like "COM Error"

Somewhere in here: http://winscp.net/eng/docs/library is a way to return failure information about opening the session.
objSession.Open(objSessionOptions)

I see SessionRemoteException and CommandExecutionResult, but I don't know how to call them to return the error information like "could not connect to server" and "invalid ID"

I'm also struggling with the synchronizedirectories function. Here's what I've got.

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; SynchronizeDirectories sample
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
objMode = ObjectClrNew( 'WinSCP.SynchronizationMode' )
Remote = ObjectClrType( 'WinSCP.SynchronizationMode', objMode.Remote )

bFalse = ObjectType( 'BOOL', 0 )
SynchronizationResult = objSession.SynchronizeDirectories( Remote, '%WebCashDir%out', 'in', bFalse )

The line above this one fails. vb.net example looks like this:

synchronizationResult = session.SynchronizeDirectories( SynchronizationMode.Remote, "d:\www", "/home/martin/public_html", False)

Thanks again!

Deana

WinBatch does not support "catching exceptions", which is a common way of handling errors in other languages.  If you need custom error handling, use IntControl 73 to capture the extended error.

Maybe use IntControl 73 Gosub option to disable default error handling. Then query exterror =  wberrorarray[6] after every statement to test for errors.  Also don't forget to call objSession.Dispose before you ever exit the script.

What is the error you see when you click on the [More Error Info] button, in the error dialog, when the SynchronizeDirectories fails?

Deana F.
Technical Support
Wilson WindowWare Inc.

Deana

SyncronizeDirectories Example:

Code (winbatch) Select
;***************************************************************************
;** FTP SynchronizeDirectories using WinSCP dotNet
;**
;** Purpose:  FTP SynchronizeDirectories using WinSCP dotNet
;** Inputs:
;** Outputs:
;** Reference:
;**       REQUIRES WinBatch 2013A or newer
;**       http://winscp.net/eng/docs/guide_dotnet
;**       http://winscp.net/eng/docs/library (WinSCP .NET Assembly and COM Library)
;**       Before starting you should:
;**       - Download and install both WinSCP and its .NET assembly/COM library (http://winscp.net/eng/download.php)
;**         By default WinScp installs to C:\Program Files (x86)\WinSCP you will need to copy the WinSCp.dll to this directory
;**       - Know how to connect to your FTP/SFTP account;
;**       - Know what WinSCP scripting commands to use for your task (e.g. file transfer).
;**       
;** Developer: Deana Falk 2014.03.19
;***************************************************************************
If Version( )< '2013A'
   Pause( 'Notice', 'Need 2013A or Newer Version of WinBatch' )
   Exit
EndIf

assemblydir = 'C:\Program Files (x86)\WinSCP'
ObjectClrOption( 'appbase', assemblydir )
ObjectClrOption ( 'use', 'WinSCP, Version=1.0.7.3446, Culture=neutral' )  ; Confirm version matches the dll

;Create an instance of the WinSCP.SessionOptions class and fill in all necessary information to allow an automatic connection and authentication of your session.
objSessionOptions = ObjectClrNew( 'WinSCP.SessionOptions' )

; Define Protocol
;  Possible values are Protocol.Sftp (default), Protocol.Scp and Protocol.Ftp.
objProtocol = ObjectClrNew( 'WinSCP.Protocol' ) 
;Sftp = ObjectClrType( 'WinSCP.Protocol', objProtocol.Sftp ) ; Sftp
Ftp = ObjectClrType( 'WinSCP.Protocol', objProtocol.Ftp ) ; Ftp
;Scp = ObjectClrType( 'WinSCP.Protocol', objProtocol.Scp ) ; Scp   

objSessionOptions.Protocol = Ftp  ; Sftp, Ftp or Scp
objSessionOptions.HostName = 'ftp.windowware.com'
objSessionOptions.UserName = 'anonymous';'user'
objSessionOptions.Password = '';'password'
If IsDefined(Sftp)
   objSessionOptions.SshHostKeyFingerprint = "ssh-rsa 1024 xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx" ;Only REQUIRED by Sftp
Endif

;Create an instance of the WinSCP.Session class. Optionally you can hook handlers of some events of the class.
objSession = ObjectClrNew("WinSCP.Session" )
;.Open the session using Session.Open method, passing instance of your WinSCP.SessionOptions.
objSession.Open(objSessionOptions)

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; SynchronizeDirectories sample
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
objMode = ObjectClrNew( 'WinSCP.SynchronizationMode' )
modeRemote = ObjectClrType( 'WinSCP.SynchronizationMode', objMode.Local )
objCriteria = ObjectClrNew( 'WinSCP.SynchronizationCriteria' )
criteriaTime = ObjectClrType( 'WinSCP.SynchronizationCriteria', objCriteria.Time )
objTransferOptions = ObjectClrNew( 'WinSCP.TransferOptions' )
objTransferMode = ObjectClrNew( 'WinSCP.TransferMode' )   
objTransferOptions.TransferMode = ObjectClrType( 'WinSCP.TransferMode', objTransferMode.Binary )
bFalse = ObjectType( 'BOOL', 0 )
localPath =  'c:\temp\'
remotePath = '/wwwftp/IntroToProgramming/'
SynchronizationResult = objSession.SynchronizeDirectories( modeRemote, localPath, remotePath, bFalse, bFalse, criteriaTime, objTransferOptions)

Shellexecute(localPath, '', '', @NORMAL, '')


; Disconnect, clean up
objSession.Dispose()
objTransferOptions = 0
objSession = 0
objProtocol = 0
objSessionOptions = 0
Exit

Deana F.
Technical Support
Wilson WindowWare Inc.

kdmoyers

more awesomeness! thanks Deana.
The mind is everything; What you think, you become.

warren


warren

Hi Deana,

I am very close now. My SFTP server is saying it does not support the default WinSCP function to preserve the file timestamp, so it won't sync the files.

I see there is an option to turn off the file sync, but once again I am stuck on syntax.

I tried this but I get an error saying CLR: Type name not found
objPreserveTimestamp = ObjectClrNew( 'WinSCP.PreserveTimestamp' )
objTransferOptions.PreserveTimestamp =  ObjectClrType( 'WinSCP.PreserveTimestamp', objPreserveTimestamp.bFalse )

I tried this and did not get an error but the function to set the timestamp did not get turned off.
bFalse = ObjectType( 'BOOL', 0 )
objTransferOptions.PreserveTimestamp =  bFalse

Thanks again,

-Warren.


Deana

Quote from: warren on March 20, 2014, 08:25:17 AM
Hi Deana,

I am very close now. My SFTP server is saying it does not support the default WinSCP function to preserve the file timestamp, so it won't sync the files.

I see there is an option to turn off the file sync, but once again I am stuck on syntax.

I tried this but I get an error saying CLR: Type name not found
objPreserveTimestamp = ObjectClrNew( 'WinSCP.PreserveTimestamp' )
objTransferOptions.PreserveTimestamp =  ObjectClrType( 'WinSCP.PreserveTimestamp', objPreserveTimestamp.bFalse )

I tried this and did not get an error but the function to set the timestamp did not get turned off.
bFalse = ObjectType( 'BOOL', 0 )
objTransferOptions.PreserveTimestamp =  bFalse

Thanks again,

-Warren.

PreserveTimestamp is a Property of the TransferOptions Class. Reference: http://winscp.net/eng/docs/library_transferoptions.

Code (winbatch) Select
; First create the (TransferOptions) Class using ObjectClrNew
objTransferOptions = ObjectClrNew( 'WinSCP.TransferOptions' )
; Define the PreserveTimestamp Property as BOOL False
objTransferOptions.PreserveTimestamp  =  ObjectType( 'BOOL', 0 ) ; define a BOOL datatype
Deana F.
Technical Support
Wilson WindowWare Inc.

warren

Looks like I was doing that right but it's not not turning the "preserving timestamp" option.

Here's my code:

Code (winbatch) Select
;Define Log File
objSession.SessionLogPath = "LogFile.log"

;.Open the session using Session.Open method, passing instance of your WinSCP.SessionOptions.
; This needs more error checking as it will fail is server or ID is wrong
;ErrorMode(@OFF)
objSession.Open(objSessionOptions)
;ErrorMode(@CANCEL)

; Synchronize Directories
bFalse = ObjectType( 'BOOL', 0 )
bTrue = ObjectType( 'BOOL', 1 )
objMode = ObjectClrNew( 'WinSCP.SynchronizationMode' )
; Local direction, changes from remote directory (source) are applied to local directory (target). Only the local directory is modified.
; modeRemote = ObjectClrType( 'WinSCP.SynchronizationMode', objMode.Local )
; Remote directon, changes from the local directory (source) are applied to the remote directory (target). Only the remote directory is modified.
modeRemote = ObjectClrType( 'WinSCP.SynchronizationMode', objMode.Remote )
objCriteria = ObjectClrNew( 'WinSCP.SynchronizationCriteria' )
; File times are compared to determine which is newer.
criteriaTime = ObjectClrType( 'WinSCP.SynchronizationCriteria', objCriteria.Time )
objTransferOptions = ObjectClrNew( 'WinSCP.TransferOptions' )
objTransferMode = ObjectClrNew( 'WinSCP.TransferMode' )   
objTransferOptions.TransferMode = ObjectClrType( 'WinSCP.TransferMode', objTransferMode.Binary )

objTransferOptions.PreserveTimestamp  =  ObjectType( 'BOOL', 0 ) ; define a BOOL datatype

SynchronizationResult = objSession.SynchronizeDirectories( modeRemote, LocalPath, remotePath, bTrue, bFalse, criteriaTime, objTransferOptions)



Here's the message from the WinSCP log file:

. 2014-03-20 13:12:09.376 Preserving timestamp [2013-08-16T19:19:14.000Z]
< 2014-03-20 13:12:09.588 Type: SSH_FXP_STATUS, Size: 44, Number: 1545
< 2014-03-20 13:12:09.588 Status code: 8, Message: 1545, Server: SSHServerAPI.SFTP.fxp_attrs, Language: 
* 2014-03-20 13:12:09.588 (ETerminal) The server does not support the operation.
* 2014-03-20 13:12:09.589 Error code: 8
* 2014-03-20 13:12:09.589 Error message from server: SSHServerAPI.SFTP.fxp_attrs
. 2014-03-20 13:12:09.589 Asking user:
. 2014-03-20 13:12:09.589 **Upload of file 'WC_REPETITIVE_APAC_2013081604183644.dat' was successful, but error occurred while setting the permissions and/or timestamp.**
. 2014-03-20 13:12:09.589
. 2014-03-20 13:12:09.589 If the problem persists, turn off setting permissions or preserving timestamp. Alternatively you can turn on 'Ignore permission errors' option. ("The server does not support the operation.
. 2014-03-20 13:12:09.589 Error code: 8
. 2014-03-20 13:12:09.590 Error message from server: SSHServerAPI.SFTP.fxp_attrs")
< 2014-03-20 13:12:09.590 Script: Upload of file 'WC_REPETITIVE_APAC_2013081604183644.dat' was successful, but error occurred while setting the permissions and/or timestamp.
< 2014-03-20 13:12:09.590 If the problem persists, turn off setting permissions or preserving timestamp. Alternatively you can turn on 'Ignore permission errors' option.
< 2014-03-20 13:12:09.590 Script: The server does not support the operation.
< 2014-03-20 13:12:09.590 Error code: 8
< 2014-03-20 13:12:09.591 Error message from server: SSHServerAPI.SFTP.fxp_attrs
* 2014-03-20 13:12:09.594 (EScpSkipFile) **Upload of file 'WC_REPETITIVE_APAC_2013081604183644.dat' was successful, but error occurred while setting the permissions and/or timestamp.**
* 2014-03-20 13:12:09.594
* 2014-03-20 13:12:09.595 If the problem persists, turn off setting permissions or preserving timestamp. Alternatively you can turn on 'Ignore permission errors' option.
* 2014-03-20 13:12:09.595 The server does not support the operation.
* 2014-03-20 13:12:09.595 Error code: 8
* 2014-03-20 13:12:09.595 Error message from server: SSHServerAPI.SFTP.fxp_attrs
. 2014-03-20 13:12:09.595 Script: Failed
> 2014-03-20 13:12:09.994 Script: exit
. 2014-03-20 13:12:09.994 Script: Exit code: 1
. 2014-03-20 13:12:09.994 Closing connection.
. 2014-03-20 13:12:09.994 Sending special code: 12
. 2014-03-20 13:12:09.994 Sent EOF message

Does that mean that this WinSCP component option is broken? I'm using the latest version of the component - Version=1.1.4.3970.

Thanks,

-Warren.

warren

Hi Deana,

One more.

In http://winscp.net/eng/docs/library_session_synchronizedirectories
is bool removeFiles   When set to true, deletes obsolete files. Cannot be used for SynchronizationMode.Both.

I tried this and a bunch of  other stuff but just got errors.   :o
; Synchronize Directories
bFalse = ObjectType( 'BOOL', 0 )
bTrue = ObjectType( 'BOOL', 1 )
objMode = ObjectClrNew( 'WinSCP.SynchronizationMode' )
modeRemote = ObjectClrType( 'WinSCP.SynchronizationMode', objMode.Remote ) ; Only the remote directory is modified.
objMode.removeFiles = bTrue

Obviously I don't get this .NET object stuff.

I really appreciate your help,

-Warren.





Deana

The error log seems to indicate that the server you are connecting to doesn't support that timestamp option:
Quote
. 2014-03-20 13:12:09.376 Preserving timestamp [2013-08-16T19:19:14.000Z]
< 2014-03-20 13:12:09.588 Type: SSH_FXP_STATUS, Size: 44, Number: 1545
< 2014-03-20 13:12:09.588 Status code: 8, Message: 1545, Server: SSHServerAPI.SFTP.fxp_attrs, Language: 
* 2014-03-20 13:12:09.588 (ETerminal) The server does not support the operation.
* 2014-03-20 13:12:09.589 Error code: 8
* 2014-03-20 13:12:09.589 Error message from server: SSHServerAPI.SFTP.fxp_attrs
. 2014-03-20 13:12:09.589 Asking user:
. 2014-03-20 13:12:09.589 **Upload of file 'WC_REPETITIVE_APAC_2013081604183644.dat' was successful, but error occurred while setting the permissions and/or timestamp.**
. 2014-03-20 13:12:09.589
. 2014-03-20 13:12:09.589 If the problem persists, turn off setting permissions or preserving timestamp. Alternatively you can turn on 'Ignore permission errors' option. ("The server does not support the operation.

Are you able to use the WinSCP GUI tool to use this timestamp setting?
Deana F.
Technical Support
Wilson WindowWare Inc.

Deana

Quote from: warren on March 20, 2014, 12:53:57 PM
Hi Deana,

One more.

In http://winscp.net/eng/docs/library_session_synchronizedirectories
is bool removeFiles   When set to true, deletes obsolete files. Cannot be used for SynchronizationMode.Both.

I tried this and a bunch of  other stuff but just got errors.   :o
; Synchronize Directories
bFalse = ObjectType( 'BOOL', 0 )
bTrue = ObjectType( 'BOOL', 1 )
objMode = ObjectClrNew( 'WinSCP.SynchronizationMode' )
modeRemote = ObjectClrType( 'WinSCP.SynchronizationMode', objMode.Remote ) ; Only the remote directory is modified.
objMode.removeFiles = bTrue

Obviously I don't get this .NET object stuff.

I really appreciate your help,

-Warren.

SynchronizeDirectories is a Method which accepts multiple parameters. removeFiles is a bool which is the 4th Parameter of SynchronizeDirectories.

http://winscp.net/eng/docs/library_session_synchronizedirectories

The previously posted code can easily change that parameter from false to true.


bFalse = ObjectType( 'BOOL', 0 )
bTrue = ObjectType( 'BOOL', -1 )
SynchronizationResult = objSession.SynchronizeDirectories( modeRemote, localPath, remotePath, bTrue, bFalse, criteriaTime, objTransferOptions)
Deana F.
Technical Support
Wilson WindowWare Inc.

warren

Ha, and I had already set that to bTrue. I was so caught up in not understanding the syntax I did not notice that.  ::)

Thanks again!

mathia

Thanks for posting this.  Exactly what I needed.  One of my vendors changed their private key which broke an application from another vendor.  I needed something to use in the interim.  This was perfect.  It's a little finicky about capitalization, but I was able to get it to work once I figured that out.

Deana

Quote from: mathia on April 16, 2014, 02:12:42 PM
Thanks for posting this.  Exactly what I needed.  One of my vendors changed their private key which broke an application from another vendor.  I needed something to use in the interim.  This was perfect.  It's a little finicky about capitalization, but I was able to get it to work once I figured that out.

Glad you found it helpful. Yes, dotNet Framework based object member names are case sensitive. 

Deana F.
Technical Support
Wilson WindowWare Inc.

warren

Hi Deana,

This has been working great for me but I have one more fun thing that I can not figure out - how to list files in an SFTP directory.

The example below shows connecting to a server and uploading a file using public key authentication instead of a password. That part works great. What I'd like to do is get a list of the files in the remote directory after I put the file.

The command
directoryList = objSession.ListDirectory('.') ; list directory
Is what I need help with. I think I need to create an object to put the directory listing into. It looks like the object is an array?

Here's my code, this should help other folks out too. There are some oddities to how the component works with WinSCP and the code below shows how I handled connection and file transfer failures.

LocalSFTPDir = "c:\mydirectory"
serverName = 'server123.bigcompany.com'
UserID = 'myUserID'
LogFile = "%LocalSFTPDir%SFTPLog.log"
PrivateKey = "C:\Program Files\putty\putty_private_key.ppk"

assemblydir = 'C:\Program Files\WinSCP'
;assemblydir = 'C:\Program Files (x86)\WinSCP'  ; 64 bit OS
ObjectClrOption( 'appbase', assemblydir )
ObjectClrOption ( 'use', 'WinSCPnet, Version=1.1.4.3970, Culture=neutral' )  ; Confirm version matches winscpnet.dll

;Create an instance of the WinSCP.SessionOptions class and fill in all necessary information to allow an automatic connection and authentication of your session.
objSessionOptions = ObjectClrNew( 'WinSCP.SessionOptions' )
objProtocol = ObjectClrNew( 'WinSCP.Protocol' ) 
Sftp = ObjectClrType( 'WinSCP.Protocol', objProtocol.Sftp )
objSessionOptions.Protocol = Sftp 
objSessionOptions.HostName = serverName 
objSessionOptions.UserName = UserID
objSessionOptions.SshPrivateKeyPath = PrivateKey
; objSessionOptions.Password = Password

;Get the host key fingerprint by using winscp GUI version to connect to the FTP server with the ID you will be using.
objSessionOptions.SshHostKeyFingerprint = "ssh-rsa 1024 00:00:00:00:00:00:00"
;objSessionOptions.GiveUpSecurityAndAcceptAnySshHostKey  ; if you don't want to mess with fingerprint, use this

;Create an new instance and open the connection
objSession = ObjectClrNew("WinSCP.Session" )
objSession.SessionLogPath = LogFile
ErrorMode(@OFF)
currError = objSession.Open(objSessionOptions)
ErrorMode(@CANCEL)

; Connectdata will be 0 if connection was successful because the log file will still be locked for writing
ErrorMode(@OFF)
ConnectData = FileGet (LogFile)
ErrorMode(@CANCEL)

If ConnectData == 0 Then
   bFalse = ObjectType( 'BOOL', 0 )
   bTrue = ObjectType( 'BOOL', 1 )
 
   objTransferOptions = ObjectClrNew( 'WinSCP.TransferOptions' )
   objTransferMode = ObjectClrNew( 'WinSCP.TransferMode' )   
   objTransferOptions.TransferMode = ObjectClrType( 'WinSCP.TransferMode', objTransferMode.Binary )
   objTransferOptions.PreserveTimestamp  =  bFalse

;objSession.RemoveFiles('test123_Back.txt')  ; delete backup. does not fail if backup doesn't exist
ErrorMode(@OFF)
;objSession.MoveFile('test123.txt', 'test123_Back.txt')  ; create new backup
objSession.PutFiles('%LocalSFTPDir%test123.txt', 'test123.txt', bFalse, objTransferOptions)
ErrorMode(@CANCEL)

directoryList = objSession.ListDirectory('.') ; list directory (this doesn't blow up, but it doesn't return file names...)

Message ("!", directoryList)

Else
   Message ("!", "Login or server access error")
EndIf

; Disconnect, clean up
   objSession.Dispose()
   objTransferMode = 0
   objTransferOptions = 0
   objSession = 0
   objProtocol = 0
   objSessionOptions = 0

ConnectData = FileGet (LogFile)

If StrIndex (ConnectData, "Error", 1, @FWDSCAN) Then message ("!", "File Transfer Error")

FileDelete (LogFile)

Exit


Thanks again,

-Warren.

Deana

Actually the ListDirectory method (http://winscp.net/eng/docs/library_session_listdirectory) returns RemoteDirectoryInfo Class (http://winscp.net/eng/docs/library_remotedirectoryinfo).

The code to list everything in the directory would look something like this:
Code (winbatch) Select

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; ListDirectory sample
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
directoryList = objSession.ListDirectory('.')
ForEach fileInfo in directoryList.Files
          name = fileInfo.Name
          length = fileInfo.Length
          perms =  fileInfo.FilePermissions
          modified =  fileInfo.LastWriteTime
          Pause( name, 'with size ':length:', permissions ':perms:' and last modification at ': modified )
Next
Deana F.
Technical Support
Wilson WindowWare Inc.

warren

Hi Deana,

That works perfectly.

Thank you again,

-Warren.

mcjathan

Hi Deana,

I'm always amazed at the capability of Winbatch, and the support!  We rely on Winbatch exclusively for our scripting needs.  Today, we were informed by one of our business partners that we can no longer can use FTP in one of our Winbatch scripts.  Instead, they're going to require SFTP.  They suggested we use WinSCP (we already use WinSCP for interactive downloading).  I did a quick google search for "Winbatch SFTP", and voilà, this thread.  Spectacular!

I do have a couple of questions:

1) When I go to http://winscp.net/eng/download.php and download the WinSCP .NET / COM library, we end up with the file winscp556automation.zip.  When I look at the contents of this zip file, I see only one *.dll file:

     WinSCPnet.dll

In the Winbatch technical article (http://tinyurl.com/qyczqu5), the *.dll file used in the sample code is instead:

     WinSCp.dll

Do I just take the code example in the Winbatch technical article and replace "WinSCp.dll" with "WinSCPnet.dll"?

2) In the zip file, the file "readme_automation.txt" describes how to register the *.dll file using the RegAsm.exe command.  Is this necessary for the code example in the Winbatch technical article?

Regards,

Jeff

td

Quote from: mcjathan on February 13, 2015, 02:09:45 PM
Hi Deana,

I'm always amazed at the capability of Winbatch, and the support!  We rely on Winbatch exclusively for our scripting needs.  Today, we were informed by one of our business partners that we can no longer can use FTP in one of our Winbatch scripts.  Instead, they're going to require SFTP.  They suggested we use WinSCP (we already use WinSCP for interactive downloading).  I did a quick google search for "Winbatch SFTP", and voilà, this thread.  Spectacular!

I do have a couple of questions:

1) When I go to http://winscp.net/eng/download.php and download the WinSCP .NET / COM library, we end up with the file winscp556automation.zip.  When I look at the contents of this zip file, I see only one *.dll file:

     WinSCPnet.dll

In the Winbatch technical article (http://tinyurl.com/qyczqu5), the *.dll file used in the sample code is instead:

     WinSCp.dll

Do I just take the code example in the Winbatch technical article and replace "WinSCp.dll" with "WinSCPnet.dll"?

Yes. The developers of the product have apparently changed the assembly file name.

Quote
2) In the zip file, the file "readme_automation.txt" describes how to register the *.dll file using the RegAsm.exe command.  Is this necessary for the code example in the Winbatch technical article?

As indicated in the WinSCP documentation, you only need to register the assembly using RegAsm.exe when you plan on using COM Interop.  In the context of WinBatch that would mean that you only need to use RegAsm.exe if you plan on using the WinBatch COM Automation subsytem instead of the CLR hosting subsystem to access the WinSCP functionality.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

mcjathan

I've got this baby up and running.  However, some followup questions:

1) Earlier in this thread, Deana posted some code to loop over all files in a remote directory (see objSession.ListDirectory).  I've been trying to figure out a way to filter out sub-directories (I only want to work with files, not folders).  Can anyone offer assistance here?

2) Also, in Deana's sample code she has this:

    transferResult = objSession.GetFiles(.....)

How do I use the return variable transferResult to determine of the transfer was successful or not?

3) Can the WinSCP Session.FileTransferProgress Event:

     http://tinyurl.com/pxm4b8g

be utilized in a Winbatch script?  If so, I'm a bit lost.  Can someone help me out here?

Regards,

Jeff

td

Quote from: mcjathan on February 24, 2015, 02:33:40 PM
I've got this baby up and running.  However, some followup questions:

1) Earlier in this thread, Deana posted some code to loop over all files in a remote directory (see objSession.ListDirectory).  I've been trying to figure out a way to filter out sub-directories (I only want to work with files, not folders).  Can anyone offer assistance here?

2) Also, in Deana's sample code she has this:

    transferResult = objSession.GetFiles(.....)

How do I use the return variable transferResult to determine of the transfer was successful or not?

You can likely answer these questions by consulting the  WinSCP documentation. 
Quote
3) Can the WinSCP Session.FileTransferProgress Event:

     http://tinyurl.com/pxm4b8g

be utilized in a Winbatch script?  If so, I'm a bit lost.  Can someone help me out here?


As mentioned in the Consolidated help file WinBatch CLR hosting does not directly support event delegates.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade