1

I am invoking the power shell script using the below C# code.

        public bool RunScript(string scriptFile)
        {
           bool b = false;
       
        using (Process process = new Process())
        {
            var startInfo = new ProcessStartInfo()
            {
                FileName = "powershell.exe",
                Arguments = $"-NoProfile -ExecutionPolicy unrestricted -file \"{scriptFile}\"",
                UseShellExecute = false
            };
            process.StartInfo = startInfo;
            process.Exited += new EventHandler(ProcessFinished);
            process.Start();
            process.WaitForExit();
            b = (process.ExitCode == 0) ? true : false;
        }
        return b;
    }

    private static void ProcessFinished(object sender, EventArgs e)
    {
        Process p = (Process)sender;
        int exitCode = p.ExitCode;
    }

In the power shell script, I am executing the below command:

Start-Process "C:\Program Files\SASHome\SASFoundation\9.4\core\sasexe\sasoact.exe" -ArgumentList "action=Submit datatype=SASFile filename=""C:\MyProject\test.sas"" progid=SAS.Application.940"

I want to wait my C# program to complete the above command. But the process is starting and immediately return with the code 0.

Ullan
  • 905
  • 4
  • 15
  • 28
  • add '-passthru' and '-wait' as parameters to start-process – Toni Sep 29 '22 at 18:00
  • Also see https://stackoverflow.com/questions/10262231/obtaining-exitcode-using-start-process-and-waitforexit-instead-of-wait and https://stackoverflow.com/questions/40360889/using-start-process-and-wait-command-in-powershell – gunr2171 Sep 29 '22 at 18:04
  • Thanks, I tried the option PassThru, but the power shell script will invoke SAS interface and returns immediately, I want to wait the process to complete the SAS job. I am not sure the best option to implement this – Ullan Sep 29 '22 at 18:32
  • Why are you calling sasoact.exe instead of just starting a NEW SAS session using the normal sas.exe command? – Tom Sep 29 '22 at 18:37
  • Please see this: https://stackoverflow.com/questions/57729365/how-to-execute-a-sas-program-using-powershell – Ullan Sep 29 '22 at 19:31
  • As I mention below, your best bet is to use SAS IntTech for C# to SAS operations. Command line via script is crude. You should have a license for Integrations Technologies. Give that a try from C#. I have done C# to SAS a lot and PS is not the way to go unless you have no choice. – AlanC Sep 30 '22 at 03:28

2 Answers2

1

Note:

  • The answer below addresses the question as asked.

  • AlanC points out the following:

    • If all your PowerShell script does is to call sasoact.exe, you don't need to call via PowerShell at all and can just invoke sasoact.exe directly, which will be noticeably faster (and you won't have problems with asynchronous invocation).

    • Additionally, to avoid side effects it is preferable to call sas.exe instead - see the comments for details.

    • An even faster, in-process option may be to use SAS Integration Technologies via its .NET API.


tl;dr

Change the content of your PowerShell script (.ps1) to the following:

# Invoke sasoact.exe *synchronously*.
# Note: If sasoact.exe isn't a *console* application, append `| Write-Output`
& 'C:\Program Files\SASHome\SASFoundation\9.4\core\sasexe\sasoact.exe' action=Submit datatype=SASFile filename='C:\MyProject\test.sas' progid=SAS.Application.940
# Report its exit code.
exit $LASTEXITCODE
  • Direct invocation of sasoact.exe ensures synchronous execution and availability of its exit code via $LASTEXITCODE, which can be passed through with exit.
  • This ensures that when your process.WaitForExit(); call returns, both sasoact.exe and your PowerShell script have finished executing and that accessing process.ExitCode subsequently reflects sasoact.exe's exit code.

Your primary problem is that Start-Process launches a program asynchronously by default; while you can make the invocation synchronous with -Wait, there is no reason to use Start-Process to begin with - see next section. (If you still want to use it, pass both -Wait and -PassThru, and use exit with the .ExitCode property on the process-info object returned:

exit (
  Start-Process -Wait -PassThru C:\Program Files\SASHome\SASFoundation\9.4\core\sasexe\sasoact.exe" -ArgumentList 'action=Submit datatype=SASFile filename="C:\MyProject\test.sas" progid=SAS.Application.940'
).ExitCode`

Typically, external executables that have CLIs are console applications, and, except in unusual circumstances, Start-Process is not the right way to invoke them.

  • The unusual circumstances where Start-Process is needed are: needing to run the program in a new console window (including with elevation) or needing to run with a different user identity. See here for guidance on when Start-Process is and isn't appropriate.

Invoking console applications directly has the following benefits:

  • synchronous invocation (whereas with Start-Process you'd have to use the -Wait switch) in the current console window (whereas Start-Process launches the process in a new window by default).[1]

  • the process exit code is reported in the automatic $LASTEXITCODE variable (with Start-Process, you'd have to use -Wait and -PassThru and query the .ExitCode property on the System.Diagnostics.Process instance that is returned)

  • the process' stdout and stderr streams are directly connected to PowerShell's output streams, allowing the output to be captured and redirected (whereas with Start-Process you can only redirect to files with -RedirectStandardOutput and -RedirectStandardError).

Note that even if the program being invoked isn't a console application, you can still force its synchronous execution and reflection of its exit code in $LASTEXITCODE by simply piping its direct invocation to | Write-Output - see this answer for more information.


[1] Given that Start-Process runs sasoact.exe in a new window by default, both powershell.exe and sasoact.exe will create a console window for themselves (assuming sasoact.exe is a console application). You can modify this behavior with the -WindowStyle parameter, but, as stated, unless you truly need to run in a separate window, there's no good reason to use Start-Process to begin with.

mklement0
  • 382,024
  • 64
  • 607
  • 775
  • I don't understand your comment. 100% Start-Process is used all of the time to execute console applications, including SAS command line. Personally, I prefer to use IntTech for C# to SAS operations but Start-Process works fine too. It isn't as robust but will work. – AlanC Sep 30 '22 at 03:25
  • The issue here is not really the PowerShell side of it, that part I get what you are saying. The issue is a SAS/C# one. In C#, I don't know how you execute a console app outside of using the process call. I think we are probably on the same page, overall, on the PS issue. However, PS shouldn't be in here at all. SAS can be called from C# directly using a SAS technology called Integration Technology. Using sasoact.exe I have never seen, so that is weird. Introducing PS into the call is unnecessary as well. Just call sas.exe directly and pass args. – AlanC Sep 30 '22 at 12:47
  • One other thing, using SAS IntTech and C#, I have loaded up hundreds of SAS sessions and just use queuing to grab them off the stack, execute, and put them back into service. Calling SAS via a console introduces the latency of the SAS startup, which can be significant. Using IntTech and queuing allows for massive numbers of SAS jobs. simultaneously. – AlanC Sep 30 '22 at 12:53
  • Please make an edit and remove sasoact.exe. It should be sas.exe by itself.. I am not even sure what sasoact.exe does but it isn't correct. – AlanC Sep 30 '22 at 19:22
  • So, I looked it up and the sasoact.exe will look for an existing SAS session that is running and use it to launch the SAS code (OLE Automation). In general, this is not what is desired. Why? Work libraries and other conflicts. If I have 10 uses submitting code, you don't want them to cross-contaminate each other. Yes, this happens and it is hard to debug. For batch jobs, best to use sas.exe. That said, easier to do it in C# using IntTech. YMMV but I have never had a need to use it and I have been a SAS consultant for a few decades. – AlanC Oct 01 '22 at 03:16
  • One other item that is critical, and relates to the OLE Automation. Even if a person uses C# with IntTech, and queuing mechanism, they still need to run a cleanup bit of code at the end to clear the libraries. I worked with a lead at SAS on it due to cross-pollination of libraries. That caused invalid results in subsequent jobs. The code should be available in my GitHub source libraries. – AlanC Oct 01 '22 at 03:24
  • All good information, @AlanC. I've updated with a brief note about `sas.exe` and a pointer to your comments here. I've cleaned up my previous comments, and I encourage you to do the same (except for at least the last 2), as appropriate. – mklement0 Oct 01 '22 at 13:19
0

You should be able to use the -Wait option on the Start-Process command.

Personally I would call SAS directly using normal command instead of trying to use the sasoact.exe tool. The sas.exe command should be in the 9.4 directory.

Here is an example showing how powershell waits for the SAS program to finish before running the next command:

PS C:\downloads> date
Thursday, September 29, 2022 3:31:41 PM
PS C:\downloads> Start-Process -FilePath "C:\Program Files\SASHome\SASFoundation\9.4\sas.exe"  -ArgumentList """C:\downloads\10seconds.sas"" -nologo -rsasuser" -Wait -WindowStyle Hidden
>> 
PS C:\downloads> date
Thursday, September 29, 2022 3:31:59 PM
PS C:\downloads>
Tom
  • 47,574
  • 2
  • 16
  • 29