Search code examples
pythonc#condaarcgis

How to start a function in a python/propy script via c#?


My python script (c:\Temp\StartPython\test.py) looks like this:

import arcpy
import string, sys, os
import ctypes

logfile ="C:\\Temp\\StartPython\\Logfile.log"

def Main(dataset):
    print ("Started with argument " + dataset)
    datei = open(logfile,'a')
    datei.write("Started with argument " + dataset)

I want to start the function "main" from the script via C#. For this I have to use the propy.bat which is used by ArcGis Pro to establish a CONDA-Enviroment for phython.

@echo off
@CALL :normalizepath scripts_path "%~dp0"

:: get the active environment from PythonEnvUtils.exe
FOR /F "delims=" %%i IN ('"%scripts_path%..\..\PythonEnvUtils.exe"') DO set CONDA_NEW_ENV=%%i

@set CONDA_SKIPCHECK=1
@set "CONDA_PREFIX=%CONDA_NEW_ENV%"

@set "CONDA_BACKUP_PATH=%PATH%"
@SET "activate_path="%scripts_path%activate.bat" "%CONDA_NEW_ENV%"
@set "deactivate_path="%scripts_path%deactivate.bat""
@call %activate_path%
python.exe %*
@set PY_ERRORLEVEL=%ERRORLEVEL%
@call %deactivate_path%
@set "PATH=%CONDA_BACKUP_PATH%"
@set "CONDA_BACKUP_PATH="

@exit /b %PY_ERRORLEVEL%

:normalizepath
    @set "%1=%~dpfn2"
    @exit /b

I was able to create a class for starting the script, which looks like this:

internal class RunProcess
{
    private Process _process;
    private StringBuilder _sbOut = new StringBuilder();
    private StringBuilder _sbError = new StringBuilder();

    public (string Output, string Error, int ErrCode) RunProcessGrabOutput(string Executable, string Arguments, string WorkingDirectory)
    {
        int exitCode = -1;
        try
        {
            _sbOut.Clear();
            _sbError.Clear();
            _process = new Process();
            _process.StartInfo.FileName = Executable;
            _process.StartInfo.UseShellExecute = false;
            _process.StartInfo.WorkingDirectory = WorkingDirectory;
            _process.StartInfo.RedirectStandardInput = true;
            _process.StartInfo.RedirectStandardOutput = true;
            _process.StartInfo.RedirectStandardError = true;
            _process.StartInfo.StandardErrorEncoding = Encoding.UTF8;
            _process.StartInfo.StandardOutputEncoding = Encoding.UTF8;
            _process.StartInfo.CreateNoWindow = true;
            _process.StartInfo.EnvironmentVariables.Add("PYTHONUNBUFFERED", "TRUE");

            if (!string.IsNullOrEmpty(Arguments))
                _process.StartInfo.Arguments = Arguments;

            _process.EnableRaisingEvents = true;
            _process.OutputDataReceived += new DataReceivedEventHandler(ProcessOutputHandler);
            _process.ErrorDataReceived += new DataReceivedEventHandler(ProcessErrorHandler);
            _process.Start();

            _process.BeginOutputReadLine();
            _process.BeginErrorReadLine();

            // You can set the priority only AFTER the you started the process.
            _process.PriorityClass = ProcessPriorityClass.BelowNormal;
            _process.WaitForExit();
            exitCode = _process.ExitCode;
        }
        catch
        {
            // This is how we indicate that something went wrong.
            throw;
        }

        return (_sbOut.ToString(), _sbError.ToString(), exitCode);
    }

    private void ProcessOutputHandler(object SendingProcess, DataReceivedEventArgs OutLine)
    {
        _sbOut.AppendLine(OutLine.Data);
    }

    private void ProcessErrorHandler(object SendingProcess, DataReceivedEventArgs OutLine)
    {
        _sbError.AppendLine(OutLine.Data);
    }
}

This is how I try to start the script:

var executable = "C:\\Program Files\\ArcGIS\\Pro\\bin\\Python\\Scripts\\propy.bat";
var workingDir = "C:\\Program Files\\ArcGIS\\Pro\\bin";

var myArguments = "\"C:\\Temp\\StartPython\\test.py\" \"C:\\somePath\" ";


var process = new RunProcess();
var processOutcome = process.RunProcessGrabOutput(executable,
myArguments, workingDir);

RunProcessGrabOutput will last ~ 1 second and afterwards no Logfile is written and the processOutcome is kinda empty:

processOutcome  ("\r\n", "\r\n", 0) (string Output, string Error, int ErrCode)

Solution

  • After some searching I found an answer here Run function from the command line

    So this is how I call the function:

    var executable = "C:\\Program Files\\ArcGIS\\Pro\\bin\\Python\\Scripts\\propy.bat";
    var workingDir = "C:\\Daten\\RadSvn\\Prototypes\\StartPythonScript\\StartPythonScript\\Scripts";
    
    var myArguments = "-c \"from Test import Main;Main('Hallo')\"";
    
    
    var process = new RunProcess();
    var processOutcome = process.RunProcessGrabOutput(executable,
    myArguments, workingDir);