Running a geoprocessing tool using background geoprocessing


Summary
This topic explains how to programmatically execute a geoprocessing tool or geoprocessing model tool in the background of an ArcGIS application so that the foreground of the application remains responsive to the user. This means that an ArcGIS control (for example, MapControl) or ArcMap remains responsive to user interaction while a tool is executing.


Geoprocessing

Since ArcGIS 9.2, a geoprocessing job (tool or model tool) can run programmatically using the Geoprocessor.Execute method. The line of code immediately following the Geoprocessor.Execute method cannot be executed until the geoprocessing tool has completed. Consequently, when a geoprocessing tool takes a significant amount of time, the application pauses and is unresponsive to the user.
For more information on foreground execution, see How to run a geoprocessing tool.
If a geoprocessing task is in a service executed on a server, the mechanism to execute the task and listen for feedback is described in How to work with geoprocessing services. To run multiple geoprocessing tasks asynchronously to each other, use multi-threaded execution on the server.

Background geoprocessing

Starting at ArcGIS 10, background geoprocessing is possible. Using the Geoprocessor.ExecuteAsync method, you can execute a tool or model tool in the background of an ArcGIS application. This means that an ArcGIS control (for example, MapControl, PageLayoutControl, GlobeControl, or SceneControl) remains responsive to user interaction while a tool is executing in a background process. In other words, data can be viewed and queried while a tool is executing.
Executing a tool using background geoprocessing, requires the following tasks:
  • Submitting the tool for execution.
  • Checking the tool's execution status.

Submitting a tool

Create the geoprocessor, then submit the parameterized tool to the ExecuteAsync method as shown in the following code example:
[C#]
ESRI.ArcGIS.Geoprocessor.Geoprocessor gp = new ESRI.ArcGIS.Geoprocessor.GeoProcessor
    ();
gp.OverwriteOutput = true;
//Register to receive the geoprocessor event when the tools have completed execution.
gp.ToolExecuted += new EventHandler < ESRI.ArcGIS.Geoprocessor.ToolExecutedEventArgs
    > (gpToolExecuted);
IGeoProcessorResult2 gpResult = gp.ExecuteAsync("CopyFeatures", parameters)as
    IGeoProcessorResult2;
[VB.NET]
Dim gp As ESRI.ArcGIS.Geoprocessor.Geoprocessor = New ESRI.ArcGIS.Geoprocessor.Geoprocessor()
gp.OverwriteOutput = True
'Register to receive the geoprocessor event when the tools have completed execution.
AddHandler gp.ToolExecuted, AddressOf gpToolExecuted
Dim gpResult As IGeoProcessorResult2 = CType(gp.ExecuteAsync("CopyFeatures", parameters), IGeoProcessorResult2)
Set the OverwriteOutput property on the geoprocessor to true if you expect to be running a tool many times with the same output parameter.
The ExecuteAsync method submits a tool to a geoprocessing queue that exists in the current process, then the tool's IGeoProcessorResult.Status property becomes esriJobStatus.esriJobWaiting.
A tool on the geoprocessing queue will not start executing until the event that added the tool to the queue has been fully processed. For example, if a button click event submits a tool, searches a features class, then changes some on-screen text, the tool starts executing after the on-screen text has changed.
Tools are executed in a background process in the order in which they were added to the queue. However, tools cannot be added to the queue unless the input to the tool exists when the tool is submitted. To work around this limitation, consider one of the following: 
The input and output parameters for each geoprocessing tool are documented in the toolbox to which a tool belongs. For a description of each toolbox, see Working with geoprocessing tool documentation and the other topics in the Geoprocessing tool reference section in the ArcGIS Desktop Help system.

Checking tool execution status

The following options are available to determine whether a tool has completed or is still executing:
  • Register and listen for the Geoprocessor.ToolExecuted events and, optionally, the other events (this is the most common option).
  • Periodically query IGeoprocessorResult.

Geoprocessor events

  • The ToolExecuting event is fired by all tools before a tool is taken from the geoprocessing queue and executed in the background. At this time, the tool's IGeoProcessorResult.Status property is still esriJobStatus.esriJobWaiting.
  • The MessagesCreated event and the ProgressChanged event can fire during tool execution. These events fire depending on which tool is executing and how much data the tool is processing.
  • The ToolExecuted event is fired by all tools when background execution has stopped.
A model tool is treated as a single tool and fires one ToolExecuting event and one ToolExecuted event as previously discussed. MessagesCreated events fire describing the progress of each tool within the model.

ToolExecuting event

If you have submitted many tools, you can determine which tool has started executing as shown in the following event handler code example. Similar code can be written in any event handler to determine which tool the event applies to.
[C#]
public void gpToolExecuting(object sender, ToolExecutingEventArgs e)
{
    IGeoProcessorResult2 result = e.GPResult as IGeoProcessorResult2;
    //Determine if this is the tool to handle this event.
    if (result.Process.Tool.Name.Equals("CopyFeatures") && result.GetInput(0)
        .GetAsText().Equals(@"c:\Europe_Roads\Europe_Roads") && result.GetOutput(0)
        .GetAsText().Equals(@"c:\Europe_Roads\Europe_Roads_Copy"))
    {
        //Application specific code.
    }
}
[VB.NET]
Public Sub gpToolExecuting(ByVal sender As Object, ByVal e As ToolExecutingEventArgs)
    Dim result As IGeoProcessorResult2 = CType(e.GPResult, IGeoProcessorResult2)
    'Determine if this is the tool to handle this event.
    If (result.Process.Tool.Name.Equals("CopyFeatures") _
           And result.GetInput(0).GetAsText().Equals("c:\Europe_Roads\Europe_Roads") _
           And result.GetOutput(0).GetAsText().Equals("c:\Europe_Roads\Europe_Roads_Copy")) Then
        'Application specific code.
    End If
End Sub

ProgressChanged event

The ProgressChanged event fires depending on which tool is executing and how much data it is processing. You can handle this event as shown in the following code example:
[C#]
public void gpProgressChanged(object sender, ProgressChangedEventArgs e)
{
    System.Windows.Forms.ProgressBar progressBar = myProgressBar;
    IGeoProcessorResult2 gpResult = (IGeoProcessorResult2)e.GPResult;
    switch (e.ProgressChangedType)
    {
        case (ProgressChangedType.Show): 
        //The tool that is running reports progress or has stopped reporting progress; make the 
        // progress bar visible if appropriate. 
        progressBar.Visible = e.Show;
        break;
        case (ProgressChangedType.Message): 
        //The application does not use these, since a tool being used reports percentage progress.
        break;
        case (ProgressChangedType.Percentage): progressBar.Value = (int)
            e.ProgressPercentage;
        break;
        default:
            throw new ApplicationException(
                "unexpected ProgressChangedEventsArgs.ProgressChangedType");
            break;
    }
}
[VB.NET]
Public Sub gpProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs)
    Dim progressBar As System.Windows.Forms.ProgressBar = myProgressBar
    Dim gpResult As IGeoProcessorResult2 = (CType(e.GPResult, IGeoProcessorResult2))
    Select Case (e.ProgressChangedType)
        Case (ProgressChangedType.Show)
            'The tool that is running reports progress or has stopped reporting progress;
            'make the progress bar visible if appropriate.
            progressBar.Visible = e.Show
        Case (ProgressChangedType.Message)
            'The application does not use these, since a tool being used reports percentage progress.
        Case (ProgressChangedType.Percentage)
            progressBar.Value = CType(e.ProgressPercentage, Integer)
        Case Else
            Throw New ApplicationException("unexpected ProgressChangedEventsArgs.ProgressChangedType")
    End Select
End Sub

ToolExecuted event

All tools fire the ToolExecuted event when background execution has stopped. The ToolExecutingEventArgs parameter has the return value, the status of a job and geoprocessing messages.
The IGeoProcessorResult interface always returns the value of a tool when it has finished executing. The return value is an object. Typically, this object is the path to the output dataset created or edited by a tool, but it can be other value types, such as a number or a Boolean. If an output for a tool is a multi-value parameter, the values are returned in a string that consists of multiple strings separated by a semicolon. 

When a tool has executed, the return value, status of the job, and all messages can be retrieved. See the following code example:
[C#]
public void gpToolExecuted(object sender, ToolExecutedEventArgs e)
{
    IGeoProcessorResult2 result = e.GPResult as IGeoProcessorResult2;
    if (result.Status.Equals(esriJobStatus.esriJobSucceeded))
    {
        //Check that there are no information or warning messages.
        if (result.MaxSeverity == 0)
        {
            //Get the return value.
            object returnValue = result.ReturnValue;
            //Application specific code, 
            //for example, find the layer to which this return value corresponds.
        }
        else
        {
            //Application specific code.
        }
    }
    else
    {
        //Get all messages.
        IGPMessages msgs = result.GetResultMessages();
        for (int i = 0; i < result.MessageCount; i++)
        {
            IGPMessage2 msg = msgs.GetMessage(i)as IGPMessage2;
            //Application specific code.
        }
    }
}
[VB.NET]
Public Sub gpToolExecuted(ByVal sender As Object, ByVal e As ToolExecutedEventArgs)
    Dim result As IGeoProcessorResult2 = CType(e.GPResult, IGeoProcessorResult2)
    If (result.Status.Equals(esriJobStatus.esriJobSucceeded)) Then
        'Check that there are no information or warning messages.
        If (result.MaxSeverity = 0) Then
            'Get the return value.
            Dim returnValue As Object = result.ReturnValue
            'Application specific code,
            'for example, find the layer to which this return value corresponds.
        Else
            'Application specific code.
        End If
    Else
        'Get all messages.
        Dim msgs As IGPMessages = result.GetResultMessages()
        Dim i As Integer
        For i = 0 To result.MessageCount
            Dim msg As IGPMessage2 = CType(msgs.GetMessage(i), IGPMessage2)
            'Application specific code.
        Next i
    End If
End Sub
For general information about message severity levels, see Geoprocessing messages.

IGeoProcessorResult

Your application can have critical sections of code that must not be interrupted or must only be interrupted at known points. Rather than be interrupted by geoprocessor events, you can maintain the IGeoProcessorResult object returned by the ExecuteAsync method and, at known times, check its job status to determine whether a tool has executed successfully.

Canceling a tool

A tool can be canceled any time, from the foreground application or from a GeoProcessor event handler. To cancel a tool, call IGeoProcessorResult2.Cancel(). While the tool is canceling, its status will be esriJobStatus.esriJobCancelling and its IGeoProcessorResult2.IsCanceled property will be true. 
Confirm that the tool is completely canceled in a ToolExecuted event handler, by testing that the IGeoProcessorResult2.Status is esriJobStatus.esriJobCancelled.

Layer and data usage during tool execution 

When a tool is waiting to execute, an application is free to use data that is input to a tool or that will be overwritten by the output of a tool. 
During tool execution, the following tasks can be accomplished:
  • Changing the visibility, select ability, and connection details of a layer input to or output from an executing geoprocessing tool.
  • Searching and selecting data input to a geoprocessing tool, for example, using the IFeatureLayer.Search and IFeatureClass.Select methods.
During tool execution, the following tasks succeed or fail depending on what the geoprocessing tool is currently doing with the data:
  • Editing table rows of input or output data in or out of an edit session.
  • Using the IFeatureLayer.Search and IFeatureClass.Select methods on output data. The set of table rows returned is undefined, since the table is in the process of being populated.
When these methods are run on output data from a geoprocessing tool, the geoprocessing tool can fail and return an esriJobStatus.esriJobFail status.
During tool execution, you cannot obtain a schema lock on input and output data, and schema changes made without a schema lock will not take effect.

Edit sessions and background geoprocessing

This section describes what actions the ArcGIS application can take if a tool is executed in the background while an edit session is in the foreground or, conversely, if an edit session is started in the foreground while geoprocessing in the background.

Start editing then execute in the background

Before executing a geoprocessing tool, check whether the user is editing the dataset's input to and output from the tool. The best practice—which suits the majority of ArcGIS applications—is, if editing, to prompt the user to save edits and stop editing. This can be achieved in a ToolExecuting event handler as shown in the following code example:
[C#]
public void gpToolExecuting(object sender, ToolExecutingEventArgs e)
{
    IGeoProcessorResult2 result = e.GPResult as IGeoProcessorResult2;
    //Get the single feature class input parameter.
    IGPParameter parameter = result.Process.InputParameters.get_Element(0)as
        IGPParameter;
    string parameterPath = "";
    if (parameter.DataType.DisplayName.Equals("FeatureClass"))
    {
        parameterPath = parameter.Value.GetAsText();
    }

    //Check whether this dataset is being edited.
    ESRI.ArcGIS.Controls.EngineEditor engineEditor = new
        ESRI.ArcGIS.Controls.EngineEditor();
    if (engineEditor.EditState == esriEngineEditState.esriEngineStateEditing &&
        engineEditor.EditWorkspace != null)
    {
        IEnumDatasetName datasetNames = engineEditor.EditWorkspace.get_DatasetNames
            (esriDatasetType.esriDTAny);
        IDatasetName name = datasetNames.Next();
        while (name != null)
        {
            string path = engineEditor.EditWorkspace.PathName + @
                "\" + name.Name;if (path.Equals(parameterPath))
            {
                //This datset is being edited. Prompt the user to stop editing.
            }
        }
    }
}
[VB.NET]
Public Sub gpToolExecuting(ByVal sender As Object, ByVal e As ToolExecutingEventArgs)
    Dim result As IGeoProcessorResult2 = CType(e.GPResult, IGeoProcessorResult2)
    'Get the single feature class input parameter.
    Dim parameter As IGPParameter = CType(result.Process.InputParameters.get_Element(0), IGPParameter)
    Dim parameterPath As String = ""
    If (parameter.DataType.DisplayName.Equals("FeatureClass")) Then
        parameterPath = parameter.Value.GetAsText()
    End If
    'Check whether this dataset is being edited.
    Dim engineEditor As ESRI.ArcGIS.Controls.EngineEditor = New ESRI.ArcGIS.Controls.EngineEditor()
    If (engineEditor.EditState = esriEngineEditState.esriEngineStateEditing And Not engineEditor.EditWorkspace Is Nothing) Then
        Dim datasetNames As IEnumDatasetName = engineEditor.EditWorkspace.get_DatasetNames(esriDatasetType.esriDTAny)
        Dim Name As IDatasetName = datasetNames.Next()
        While (Not Name Is Nothing)
            Dim Path As String = engineEditor.EditWorkspace.PathName + "\" + Name.Name
            If (Path.Equals(parameterPath)) Then
                'This datset is being edited. Prompt the user to stop editing.
            End If
        End While
    End If
End Sub

End Class
If editing is not stopped, the geoprocessing tool fails. This failure is confirmed by checking for the esriJobStatus.esriJobFail status and reading the IGeoProcessorResult messages in a ToolExecuted event handler.
Another option is to let the user continue editing and cancel the tool or let it fail. You can then revert to executing the geoprocessing tool in the foreground. In the ToolExecuting event handler, call IGeoProcessorResult2.Cancel(), then in the ToolExecuted event handler, check that the tool is canceled using IGeoProcessorResult2.Status = esriJobStatus.esriJobCancelled, then use the Geoprocessor.Execute method. This foreground execution prevents the user from interacting with the application until the tool has completed. This option might be preferable to requiring the user to save or discard edits, especially if the foreground geoprocessing completes in a timely manner. All edits that the tool has made in the edit workspace are saved when editing is stopped.

Execute in the background then start editing

If your edited map contains layers and source data that are input to or output from geoprocessing tools, StartEditing will not be blocked. However, if your edited map contains only layers and source data that are the output of geoprocessing tools, StartEditing will be blocked.
Start editing is only blocked by the background process during tool execution. It is possible that your application will attempt to start editing before it can be blocked. Therefore, before the user or application starts editing data, check whether or not this data is (or will become) the input to or output from any geoprocessing tool that is queued for execution or is executing. To do this, maintain a list of IGeoProcessorResult2 objects with the result of each geoprocessing tool you have queued, then catch the IEditEvents.OnStartEditing or IWorkspaceEditEvents.OnStartEditing event.
If any tool is running or is scheduled to run, check each tool's parameters (from the list of IGeoProcessorResult2 objects) against the edited workspace, as shown in the previous code example.


See Also:

How to run a geoprocessing tool
How to work with geoprocessing services
Geoprocessing
Using geoprocessing




To use the code in this topic, reference the following assemblies in your Visual Studio project. In the code files, you will need using (C#) or Imports (VB .NET) directives for the corresponding namespaces (given in parenthesis below if different from the assembly name):
Development licensing Deployment licensing
Engine Developer Kit Engine
ArcGIS for Desktop Basic ArcGIS for Desktop Basic
ArcGIS for Desktop Standard ArcGIS for Desktop Standard
ArcGIS for Desktop Advanced ArcGIS for Desktop Advanced