PowerBuilderからWinSCPを使う

 こんにちは、石川さんです。開発中のPowerBuilderのアプリケーションからWinSCPを使って通信する方法を調べたのですが、ちょっと難しかったので記録しておきます。誰かのお役に立ちますように。実行環境は64ビット版のWindows 10で、PowerBuilder 2017 R3の32ビットアプリケーションを開発しています。

 階層化したフォルダーへファイルを展開する必要があったので、複雑なことができるよう.NetアセンブリをOLEオブジェクトとして利用できる方法についてのお話になります。単なるEXEとして実行する方法ではありません。

セットアップについて(基本的にはWinSCPのホームページに書いてありました)

 英語が読める方は、こちらを読むようにお願いします。手順としては、

  • ダウンロードして
  • 解凍して
  • OLEオブジェクトとして利用できるように登録します

です。

 まずは、ダウンロードします。こちらのページにありました。PowerBuilderからの利用には、「.NET assembly / COM library」を選択すれば使えるようです。以下のリンクをクリックしてファイルをダウンロードします。

 ダウンロードされるファイルは「WinSCP-X.X.X-Automation.zip」のような名前になっています。ぼくがダウンロードしたときは、「WinSCP-5.21.7-Automation.zip」でした。

 zipファイルを解凍した中にある、「WinSCPnet.dll」が本体、「WinSCP.exe」が実行形式のファイルだそうです。dllを確認したところ、32ビット版でした。開発中のアプリケーションが32ビット版なので、ここは一致している必要があるような気がします。ぼくの環境ではこの二つのファイルを展開するだけで使えるようになりました。

 次にOLEオブジェクトとして利用できるようにする手順ですが、以下のコマンドを実行します。WinSCPnet.dllのあるフォルダで実行ですよ!実行するとレジストリに登録されるようです。レジストリの中まではざっとしか確認していません。

%WINDIR%\Microsoft.NET\Framework\<version>\RegAsm.exe WinSCPnet.dll /codebase /tlb

REM ぼくの環境では以下のとおりでした。
C:\Windows\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe WinSCPnet.dll /codebase /tlb

 これで、使えるようになりました。あとは、PowerBuilderでコーディングするだけですね。ちなみに、実行すると「WinSCPnet.tlb」というファイルができました。登録を解除するには「/tlb」ではなく「/unregister」を指定して実行すればオッケーのようです。このあたり、あまり詳しくありません。試しに「/unregister」を指定したコマンドを実行してから開発中のプログラムを実行したらエラーがでていました。

PowerBuilderのソースコード

 ここのソースコードをコピペして動かしてみました。ただし、そのままだと動かなかったので、少し変更しました。変更部分を含めて再登録しておきます。ボタンをつくって、クリックしたときのイベント(clicked)に記載して確認しています。

oleobject s_ftp // for WinSCP.Session
oleobject s_opt // for WinSCP.SessionOptions
oleobject s_trans // for WinSCP.TransferOptions, i just used default values (Binary transfer and overwrite options)
int return_code

s_ftp = CREATE oleobject

return_code = s_ftp.connectToNewObject("WinSCP.Session")

if return_code <> 0 then
   messagebox("Error", "S_FTP Component installation error")
   return - 1
end if   

s_opt = CREATE oleobject
return_code = s_opt.connectToNewObject("WinSCP.SessionOptions")

if return_code <> 0 then
   messagebox("Error", "Seasion Options  Component installation error")
   return - 1
end if      

s_trans = CREATE oleobject
return_code = s_trans.connectToNewObject("WinSCP.TransferOptions")

if return_code <> 0 then
   messagebox("Error", "Transfer Options Component installation error")
   return - 1
end if      

s_opt.protocol = 2 // 0:SFTP 1:SCP 2:FTP WEBDAV:3
s_opt.hostname = "CHANGE YOUR IP" // server IP
s_opt.UserName = "CHANGE YOUR ID" // user id
s_opt.Password = "CHANGE YOUR PASSWORD" // user pass
//s_opt.GiveUpSecurityAndAcceptAnySshHostKey = true  // this is not save, instead server key should be used
//s_opt.SshHostKeyFingerprint = "ssh-rsa 2048 xxxxxxxxxxx..."

try
   any result
   result = s_ftp.open(s_opt) 
//   return  integer(result) ←これがあって、下のputfilesが実行できなかったのでコメントアウト
catch (runtimeerror  e)
  messagebox("Error",e.getMessage())
return -1
end try

Ll_rtn = integer (s_ftp.putfiles("C:\poi.txt", "/FTP/poi.txt", false,s_trans ) ) // ローカルからリモートへコピー

IF Ll_rtn < 0 THEN
   as_msg   =   "File Upload Error(FTP)!"
END IF

s_ftp.close()

以上です!このあとに続く、これ以上の開発にはこちらのライブラリの情報が必要です。

皆さんの環境でもうまいこと動きますように。

追記

 実際に開発を進めていって、s_ftp.putfiles()の戻り値がうまく取得できていないことが発覚しました。これではエラーハンドリングできません。と、いうことで、後半をちょっと追記しますと、、、こんな感じに仕上がりました。

	OLEObject s_result	// for WinSCP.TransferOperationResult
	//ファイル送信
	s_result	=	s_ftp.putfiles(as_fileName, ls_folder, false, s_trans )
	
	if	s_result.isSuccess	then
		s_ftp.close()
		return	""
	else
		s_result.Check()
	end	if

catch (oleRuntimeError oe)
	destroy	s_trans
	destroy	s_opt
	destroy	s_ftp
	return	"WinSCPの処理に失敗しました。~n~n【詳細】~n"+oe.Description+"~n~n"+oe.getMessage()+")"
catch (runtimeerror  e)
	destroy	s_trans
	destroy	s_opt
	destroy	s_ftp
	return	"WinSCPの処理に失敗しました。(エラーメッセージ:"+e.getMessage()+")"
end try

 戻り値を取得するためのOLEObject変数s_resultを設定し、s_result.isSuccessをチェックすることで成功か失敗か、ということが判断できます。失敗したときは、s_result.Check()を呼び出すことでOLEランタイムエラーを発生してくれます。このs_result.isSuccessだけでも充分だと思いますが、一箇所でハンドリングしたいときなどは、いきなりs_result.Check()を呼び出すとよいと思います。