ArcGIS Desktop

  • ArcGIS Pro
  • ArcMap

  • My Profile
  • Help
  • Sign Out
ArcGIS Desktop

ArcGIS Online

The mapping platform for your organization

ArcGIS Desktop

A complete professional GIS

ArcGIS Enterprise

GIS in your enterprise

ArcGIS Developers

Tools to build location-aware apps

ArcGIS Solutions

Free template maps and apps for your industry

ArcGIS Marketplace

Get apps and data for your organization

  • Documentation
  • Support
Esri
  • Sign In
user
  • My Profile
  • Sign Out

ArcMap

  • Home
  • Get Started
  • Map
  • Analyze
  • Manage Data
  • Tools
  • Extensions

Calling a DLL from a script tool

  • How it works
  • The details

New in Python version 2.5 is the ctypes, a foreign function library. It provides C-compatible data types and allows calling functions in DLLs or shared libraries. Using the ctypes module in Python allows ArcObjects code written in C++ to be used in a geoprocessing script tool.

Using ctypes in Python allows you to easily change the parameters and types the script tool expects without having to recompile our C++ code. The ctypes module supports any C-callable function with basic data types, such as char, int, float, and double as well as structs and pointers.

There are many benefits with calling a DLL from a Python script. You can leverage the finely grained ArcObjects classes in your geoprocessing tasks, your intellectual property is protected, and it is much easier to implement than having to use the IGPFunction2 and IGPFunctionFactory interfaces. You create your object in Python and call the execute method, passing in the required parameters from the script using the geoprocessing framework.

How it works

The steps are the following:

Create a C++ Win32 project in Visual Studio 2008 that exports a simple function with the prototype:

int GpExecutetool(char* parameter1, char* parameter2)

Be sure to include the ArcGIS\com\directory in the project and import the ArcObjects .olb files.

Create a script tool in a custom toolbox that validates the two parameters and passes them to the script.

Your Python script will do the following:

  • Import arcpy and ctypes.
  • Get the parameters from the script tool.
  • Import the DLL into memory.
  • Get a pointer to the function in the DLL.
  • Specify the required argument types of functions exported from DLLs by setting the argtypes attribute as well as the return type.
  • Pass the parameters to the C++ code in the DLL.

The details

The C++ project (named GPToolAsSimpleDLL for this example) is a simple Win32 project that adds an AREA field to the feature class and calculates the value.

The header file

#ifdef GPTOOLASSIMPLEDLL_EXPORTS
#define GPTOOLASSIMPLEDLL_API extern "C"__declspec(dllexport)
#else
#define GPTOOLASSIMPLEDLL_API extern "C"__declspec(dllimport)
#endif

GPTOOLASSIMPLEDLL_API int GPexecutetool(char*, char*);

The GPToolAsSimpleDLL source file opens the specified polygon feature class and sets the specified field to the area of each polygon feature. Every geoprocessing function you write could be implemented with a simple C function entry point, all in the same DLL, along with script tool companions to expose each function to ArcToolbox.

GPTOOLASSIMPLEDLL_API int GPexecutetool(char* featureclassPath, char* fieldname)
{
    // Convert char*s to bstring
    _bstr_t catalogPath;
    catalogPath = featureclasspath;
    _bstr_t newfieldname;
    newfieldname = fieldname;

    // Coinitialize GP utilities class
    IGPUtilitiesPtr ipUtil(CLSID_GPUtilities);

    // Feature class holder
    IFeatureClassPtr ipFeatureclass(0);

    HRESULT hr;

    // Try to fetch feature class from catalog path
    if (FAILED(hr = ipUtil->OpenFeatureClassFromString(catalogPath, &ipFeatureclass)))
    {
        return -1;
    }

    // Index position of the specified field
   	long fieldIndex;
   	ipFeatureclass->FindField(newfieldname, &fieldIndex);

    // Set up query filter and feature cursor
   	IQueryFilterPtr ipFilter(CLSID_QueryFilter);
   	IFeatureCursorPtr ipCursor;
   	IFeaturePtr ipRow;
   	IGeometryPtr ipShape;

    // Open search cursor on feature class
   	ipFeatureclass->Search(ipFilter, VARIANT_FALSE, &ipCursor);

    // Iterate
    esriGeometryType gt;
    for (ipCursor->NextFeature(&ipRow);
       		ipRow != NULL;
         ipCursor->NextFeature(&ipRow))
    {
         // Get row's associated geometry
		       ipRow->get_Shape(&ipShape);

		       // Ensure we've got a polygon
		       ipShape->get_GeometryType(&gt);
		       if (gt != esriGeometryPolygon)
			          return -2;

		       // Get area
		       IAreaPtr ipArea(ipShape);
         double area;
		       ipArea->get_Area(&area);

		       // Pop double into a variant
         VARIANT value;
		       value.vt = VT_R8;
		       value.dblVal = area;

		       // Set double variant onto target field
		       ipRow->put_Value(fieldIndex, value);

	       	// Save
	       	ipRow->Store();
    }

    return S_OK;
}

The Python script acts as a broker, accepting the two parameters from the script tool as text and sending them to the DLL function as char*, zero-terminated strings. The script also uses the Add Field geoprocessing tool before the function in the DLL is called. This can make your workflow more robust by implementing your proprietary functionality in C++ and implementing the common tasks in Python.

import arcpy
import ctypes

# Get the parameters from the script tool dialog
#
shp = arcpy.GetParameterAsText(0)
fieldName = arcpy.GetParameterAsText(1)

# See if the field already exists in the feature class.
#   If not, add it.
if len(arcpy.ListFields(shp, fieldName)) == 0:
    arcpy.AddField_management(shp, fieldName, "DOUBLE")

# Import DLL into memory and get a pointer to the function
#   Note: be sure the DLL is in your Python search path 
#
dll = ctypes.cdll.GPToolAsSimpleDLL
perform_function = dll.GPExecutetool

# Tell ctypes the function accepts two char* arguments
#
perform_function.argtypes = [ctypes.c_char_p, ctypes.c_char_p]

# Tell ctypes the function return type
#
perform_function.restype = ctypes.c_int

# Call the function in the DLL
#
retval = perform_function(shp, fieldName)

# Check the return value.  If a 0 returned, success!
#
if retval == 0:
    arcpy.AddMessage("Success")
elif retval == 1:
    arcpy.AddError("Unable to open " + shp)
elif retval == 2:
    arcpy.AddError("Not a polygon feature class")

ArcGIS Desktop

  • Home
  • Documentation
  • Support

ArcGIS

  • ArcGIS Online
  • ArcGIS Desktop
  • ArcGIS Enterprise
  • ArcGIS
  • ArcGIS Developer
  • ArcGIS Solutions
  • ArcGIS Marketplace

About Esri

  • About Us
  • Careers
  • Esri Blog
  • User Conference
  • Developer Summit
Esri
Tell us what you think.
Copyright © 2021 Esri. | Privacy | Legal