tags:

views:

376

answers:

4

Currently, I am working in a delphi application. I need to run perl script from the delphi application. To be more clear, manually we are executing the perl script in unix window. Now I need to automate the execution of that perl script from the application.

I tried using ShellExecute:

ShellExecute(Handle, 'open', PChar('C:\loaderperl.bat'), nil, nil, SW_SHOW);

The window blinks for a second and closed. I don't know whether the script executed or not.

Hi, Thanks for all your answers and sorry for my late reply. Still I am struggling with this automation. Now I will explain the process clearly, at present I open the putty window, connect to the remote machine and execute the perl script. The perl script in turn calls an store procedure and updates the table. Now I want to do above explained process automatically by clicking a button. So when I click a button, it should call a function which connects to the remote machine and then executes the perl script. Am I clear to you??? Please help to solve this. I need this code in delphi

A: 

Start rsh or whatever your unix windows is with the relevant arguments (perl interpreter + script location+name) using shellexecute?

Marco van de Voort
+1  A: 

(Edited in response to comments below.)

There are a couple of steps to this: first, actually run the script; second, check if it successfully ran (both that it managed to start a new process and that that process succeeded.)

Part 1, running the script:

To run the script from your Delphi app, use ShellExecuteEx with the "open" verb should work (more details in flags below.) You could execute the script itself if it's associated with your Perl executable, or run your Perl exe with the script as a command line argument.

This article looks like a good starting point for using this API and its simpler cousin ShellExecute.

The ShellExecute[Ex] functions usually expect your application to run a message loop afterwards. It's unlikely your application isn't, so long as after the user clicks the button to start this your app sits idle, but if it isn't going to for some reason you should pass the SEE_MASK_NOASYNC flag. MSDN has lots of info.

Part 2, checking if it executed successfully and succeeded in its task:

To see if the script executed, you should get a process handle to the started process and, if there was a new process, check the exit code from that. You should modify your script to return a non-zero code if its task fails. (I'm not familiar with Perl so I can't specifically tell you how, but generally it's the return value from the "main" function or other first/startup function.) You can do this by:

  • Pass in the SEE_MASK_NOCLOSEPROCESS flag to ShellExecuteEx. That will fill the hProcess member of the struct you pass in with a process handle, if it successfully started a process (if not, check GetLastError to find out why.) If this is all you need to know (just if it started, nothing about if it worked) then you're done.
  • Optional: wait for the process to end by using WaitForSingleObject(YourParamStruct.hProcess, INFINITE) (if you got a valid hProcess, of course)
  • Call GetExitCodeProcess to find the exit code. (Check for it returning STILL_ACTIVE, and if it is still running wait and try again.) Normally, it should exit with 0 to indicate success. You should modify your script so that if something goes wrong it returns non-zero. You can make the codes as simple or complex as you want - eg, return 1 for any error, or make a number of codes broken up by the type of error. (Avoid using 259, because that's the value of STILL_ACTIVE - doing so might lead your Delphi code into an infinite loop, depending how it's coded, thinking it's still waiting to get an exit code because the Perl script is still running when it's not.)

Your Delphi app can then check this code to see if the Perl script worked or not and do something in response if it failed - log a message, tell the user, etc.

One other thing - you say you just want to know if the script executed or not and the above should provide that. Given it's a Perl script and runs on the command line, one other thing you might want to do is get its output, either to scan for errors if you don't return an error code, or to log or show the user. You can do this by running it via CreateProcess and capturing the output. From what you say this is probably overkill for what you're after, though!

David M
I would always execute a shell and pass it the script name as a parameter, to allow for wildcards, input / output redirection and the like. The shebang line will make sure the perl interpreter is started.
mghie
That never occurred to me, mghie - good point!
David M
Automate means human interruption should be nil. While clicking a button, the code should automatically execute the perl script. I tried using ShellExecute:ShellExecute(Handle,'open',Pchar('C:\loaderperl.bat'),nil,nil,SW_SHOW) ;The window blinks for a second and closed. I dont know whether the script executed or not. Could you please clear me in this?
JVNR
Thanks for clarifying, JVNR. I've just edited this answer to hopefully answer how you can know if it executed or not (and if the script worked - it's quite possible for the script to run, but something to go wrong in it, which you probably want to know too.)
David M
Hi, Thanks for all your answers and sorry for my late reply. Still I am struggling with this automation. Now I will explain the process clearly, at present I open the putty window, connect to the remote machine and execute the perl script. The perl script in turn calls an store procedure and updates the table. Now I want to do above explained process automatically by clicking a button. So when I click a button, it should call a function which connects to the remote machine and then executes the perl script. Am I clear to you??? Please help to solve this. I need this code in delphi.
JVNR
This is a late reply to your late reply, JVNR - sorry for that too. I've been overseas for a couple of months. I think you're in effect asking a completely different question than what you appeared to ask here, so the answer would be completely different. It would be best to ask as a new SO question, perhaps referring to this one and your comment to clarify.
David M
A: 

You need to call external program (perl interpreter to execute on local machine, ssh or smth like this to execute on remote machine). Here is procedure that calls external program and waits until it ends (if you don't need to wait until it ends, remove WaitForSingleObject or just call ShellExecute as other people suggested).

UPDATE: To see script output, try to use solution from this article

function ExecAndWait(const FileName, Params: ShortString; const WinState: Word): boolean; export; 
var 
  StartInfo: TStartupInfo; 
  ProcInfo: TProcessInformation; 
  CmdLine: ShortString; 
begin 
  { Put the name of file between quotes, due to spaces in names of files in system Win9x } 
  CmdLine := '"' + Filename + '" ' + Params; 
  FillChar(StartInfo, SizeOf(StartInfo), #0); 
  with StartInfo do 
  begin 
    cb := SizeOf(SUInfo); 
    dwFlags := STARTF_USESHOWWINDOW; 
    wShowWindow := WinState; 
  end; 
  Result := CreateProcess(nil, PChar( String( CmdLine ) ), nil, nil, false, 
                          CREATE_NEW_CONSOLE or NORMAL_PRIORITY_CLASS, nil, 
                          PChar(ExtractFilePath(Filename)),StartInfo,ProcInfo);
  { Wait the finish of program } 
  if Result then 
  begin 
    WaitForSingleObject(ProcInfo.hProcess, INFINITE); 
    { Free the Handles } 
    CloseHandle(ProcInfo.hProcess); 
    CloseHandle(ProcInfo.hThread); 
  end; 
end;
Dmitry
A: 

If you want to ShellExecute() a batch file you need to execute cmd.exe and pass the script as a parameter, like so:

ShellExecute(Handle, 'open', 'cmd.exe', '/C C:\loaderperl.bat', nil, SW_SHOW);

You can use output redirection to capture the script output in a file. If you want to see whether the script was executed successfully you can (for testing purposes) add pause to the end of it.

mghie