3
votes

I would like a Win32 process to launch an external script and be able to retrieve the ERRORLEVEL that it returns.

The other way around is quite easy. Just use exit(errorcode) or return errorcode; in your Win32 app and the calling script gets its ERRORLEVEL value set properly.

But, from a Win32 calling application, getting the script errorcode is not quite the same.

I tried using CreateProcess() and calling GetExitCodeProcess(), but it always returns 0 and not the actual value of ERRORLEVEL. Even if I end my called script with exit %ERRORLEVEL%

My best guess is that a script is not a process per-say. Most probably CMD.EXE is running instead and most likely always ending with a ExitCode of 0. I know that ERRORLEVEL is not the same as the process ExitCode value, I would have hoped that CMD.EXE would have mirrored it.

EDIT:

Sorry I asked! I just found MY problem. I was using exit /b errorcode instead of exit errorcode in my batch file. It seems that the /b options has the advantage of only closing the running script, not CMD.EXE when you are testing from command line. But the disadvantage of not setting the proper ExitCode for CMD.EXE.

So, for posterity what I am doing is:

int LaunchScript(TCHAR *pCmdLineParam)
{
    bool bResult;
    PROCESS_INFORMATION pi;
    STARTUPINFO si;

    memset(&si, 0, sizeof(si));
    si.cb = sizeof(si);

    TCHAR   cmdLine[256];
    _tcscpy(cmdLine,L"Test.cmd");
    _tcscat(cmdLine,L" ");
    _tcscat(cmdLine,pCmdLineParam);

    _tprintf(L"Process executing: %s\n",cmdLine);

    bResult = CreateProcess(NULL, cmdLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)?true:false;
    if (!bResult) {
        _tprintf(L"CreateProcess() error - %d", GetLastError());
        return -1;
    }

    DWORD result = WaitForSingleObject(pi.hProcess,15000);
    if( result == WAIT_TIMEOUT ) {
        return -2;
    }

    DWORD exitCode=0;
    if( !GetExitCodeProcess(pi.hProcess,&exitCode) ) {
        _tprintf(L"GetExitCodeProcess() error - %d", GetLastError());
        return -1;
    }

    _tprintf(L"Process exitcode=%d\n",exitCode);

    return exitCode;
}

My "entry point" batch file looks like:

@call %*
@exit %ERRORLEVEL%

And I am passing my script to run as a parameter to the entry point script. The other "sub-scripts" files can either call exit /b or exit, because everything is covered.

3

3 Answers

1
votes

This works for me:

int DoDOS(string parms)
{

Process p=new Process();
ProcessStartInfo ProcInfo=new ProcessStartInfo();

ProcInfo.Arguments="/C "+parms;
ProcInfo.CreateNoWindow=true;
ProcInfo.ErrorDialog=false;
ProcInfo.ErrorDialogParentHandle=IntPtr.Zero;
ProcInfo.FileName="cmd.exe";
ProcInfo.RedirectStandardError=false;
ProcInfo.RedirectStandardInput=false;
ProcInfo.RedirectStandardOutput=false;
ProcInfo.UseShellExecute=false;
ProcInfo.Verb="";
ProcInfo.WindowStyle=ProcessWindowStyle.Hidden;
p=Process.Start(ProcInfo);

while (!p.HasExited)
      {
      if (laRunning.Text!=RunningTxt) laRunning.Text=RunningTxt;
      else                            laRunning.Text="";
      Application.DoEvents();
      Thread.Sleep(500);
      }

return p.ExitCode; 
}
1
votes

Just a shot in the dark (since I'm at home and have no windows around):

We use 'cmd /c call <script>' at work. Maybe it works for your problem.

-1
votes

Try passing this as the lpCommandLine parameter of CreateProcess:

cmd /v:on /k <script_name> & exit !errorlevel!

It will turn on delayed environment variable expansion (otherwise %ERRORLEVEL% expands before executing <script_name>) and explicitly return the ERRORLEVEL returned by the script as the cmd.exe's return code.