WinSCP Wrapper (Managed c++)
Here is a little code I wrote to help me access WinSCP via Managed C++.
The code is extremely simple. A worker thread is spawned which reads from a message packet. Even though this class uses a worker thread it is technically synchronous. You can only run a new WinSCP command when the last one has been completed. The basic logic is thus: Assign Command to WinSCP -> WinSCP Runs Command Automatically -> WinSCP Returns to a MessageComplete delegate -> Assigned a Command to WinSCP
There isn’t a particular reason for my lack of true asynchronous support other than a lack of time for proper coding. The time saved by running commands from a queue is negligible given how little I do with the data I request in my apps. That’s also why you’ll see the beginnings of thread safety (locking) but why it isn’t complete. Since everything has to run in a specific order there is no need to lock any variables.
The code for this project is being attached directly to this post rather than in my SVN account.
Usage:
ref class ScpRun { public: ScpRun(){ _commandList = gcnew System::Collections::Generic::List<WinScpCmd^>; _scp = gcnew WinScpWrapper(); _this = this; _account = gcnew String(""); }; bool Done(){return _bDone;} void Run(); void Stop(){_this->_scp->RequestStop();} ///Get/Set WinSCP account name property String ^Account { String ^ get(){return _account;} void set(System::String ^ s){_account = s;} }; protected: ///WinSCP message is complete static void ScpMessageComplete(System::Object ^sender, WinScpEventArgs ^args); private: ///WinSCP wrapper API WinScpWrapper ^_scp; ///Command stack (each return message from WinSCP) System::Collections::Generic::List<WinScpCmd^> ^_commandList; ///Self-referential pointer for worker thread private: static ScpRun ^_this; ///Worker thread complete? bool _bDone; ///WinSCP account name System::String ^_account; };
There isn’t much going on here. We create a generic WinSCP handler class. In my example we create a command stack (just a list), each successive command is sent to WinSCP when the previous one is complete.
ScpRun::Run
void ScpRun::Run(){ _bDone = false; { ///Create command list //The very first call MUST be to open an account name. I suggest setting up an easy name using WinSCP that you can reference here. _commandList->Add(gcnew WinScpCmd(WSC_OPEN,_account)); //Next we'll add a command to print the current working directory _commandList->Add(gcnew WinScpCmd(WSC_PWD,"")); //Now lets dump the contents of our current directory _commandList->Add(gcnew WinScpCmd(WSC_LS,"")); //Lastly be sure to kill your connection _commandList->Add(gcnew WinScpCmd(WSC_CLOSE,"")); } //Pull the first command off our command stack and call WinSCP WinScpCmd ^tmp = _commandList[0]; _commandList->RemoveAt(0); //Here we send our command to WinSCP, arguments are parsed by the class scp->wsc_call(tmp->Command,tmp->Arguments); //Be sure to assigned a MessageComplete callback function scp->WSCMessageComplete += gcnew WinScpWrapper::MessageComplete(&ScpRun::ScpMessageComplete); //Tell WinSCPWrapper it's time to start scp->RequestStart(); }
Again, all real simple code. We’ve added 4 commands to our command stack and we’ve sent the first off to be run. When that command is complete (successfully or not) it will push out an event to any MessageComplete handlers. I should note here that there isn’t any sort of timeout value. An assumption is made (wrongfully or not) that WinSCP will always return a value.
ScpRun::ScpMessageComplete
void ScpRun::ScpMessageComplete(System::Object ^sender, WinScpEventArgs ^args){ //Arguments only have 2 states, success or failure. if(!args->_bSuccess){ switch(args->_trigger){ case WSC_PWD: case WSC_LS: case WSC_OPEN: case WSC_CLOSE: printf("Crap-sicle our command has failed."); break; default: break; } printf("[Command %s failed.]\n",args->_fullCommand); } else { switch(args->_trigger){ case WSC_LS:{ break; case WSC_PWD: break; default: printf("Command %s complete.\n",args->_fullCommand); printf(" - Value: %s.\n",args->_value); break; } } ///If command list contains more commands, run them. (Demo only) if(_this->_commandList->Count > 0){ WinScpCmd ^tmp = _this->_commandList[0]; _this->_commandList->RemoveAt(0); _this->scp->wsc_call(tmp->Command,tmp->Arguments); } else { _this->_bDone = true; } }
Here we handle return messages from our WinSCP wrapper. It would be advisable to add some code here to handle an error from WSC_OPEN that tells the command stack to stop issue commands and set _bDone to true;
The only remaining action is to run our handler class:
ScpRun ^scp = gcnew ScpRun (); scp->Account = "dummy_account_name"; scp->Run(); //loop until done while(!scp->Done()){ Threading::Thread::Sleep(15); } //Tell SCP to stop which in turn tells our WinSCP wrapper to kill its worker thread. scp->Stop();
I should mention the WinScpWrapper files. There are some lines that look like the following:
#include <GIniWrapper.h> //gcnew String(config.getValue.....
You’ll have to remove references to
