こんにちは、石川さんです。開発中の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()
を呼び出すとよいと思います。