Aufrufen einer DLL aus einem Skriptwerkzeug
Die externe Funktionsbibliothek ctypes ist neu in Python 2.5. Sie stellt C-kompatible Datentypen bereit und ermöglicht das Aufrufen von Funktionen in DLLs oder freigegebenen Bibliotheken. Mit dem Modul ctypes in Python kann ArcObjects-Code, der in C++ verfasst wurde, in einem Geoverarbeitungsskriptwerkzeug verwendet werden.
Durch die Verwendung von ctypes in Python können Sie die Parameter und Typen leicht ändern, die vom Skriptwerkzeug erwartet werden, ohne den C++-Code neu kompilieren zu müssen. Das Modul ctypes unterstützt alle aufrufbaren C-Funktionen mit grundlegenden Datentypen, beispielsweise char, int, float und double sowie structs und Zeiger. Weitere Informationen zum Modul "ctypes" in Python 2.6.2 finden Sie unter 16.15 ctypes—A foreign function library for Python.
Das Aufrufen einer DLL aus einem Python-Skript bietet eine Reihe von Vorteilen. Sie können die komplexen ArcObjects-Klassen in Ihren Geoverarbeitungs-Tasks verwenden, die Rechte an Ihrem geistigen Eigentum bleiben gewahrt, und die Implementierung ist, verglichen mit einer Verwendung der Schnittstelle IGPFunction2 und der Schnittstelle IGPFunctionFactory, viel leichter. Erstellen Sie das Objekt in Python, und rufen Sie die execute-Methode auf. Übergeben Sie anschließend die erforderlichen Parameter vom Skript, und verwenden Sie dazu die Geoverarbeitungsumgebung.
Funktionsweise
Die Schritte lauten wie folgt:
Erstellen Sie in Visual Studio 2008 ein Win32-Projekt mit C++, das eine einfache Funktion mit dem Prototyp exportiert:
int GpExecutetool(char* parameter1, char* parameter2)
Stellen Sie sicher, dass das Verzeichnis "ArcGIS\com\" im Projekt enthalten ist, und importieren Sie die ArcObjects-OLB-Dateien.
Erstellen Sie ein Skriptwerkzeug in einer benutzerdefinierten Toolbox, mit dem die beiden Parameter überprüft und an das Skript übergeben werden.
Das Python-Skript führt folgende Aktionen aus:
- Importieren von arcpy und ctypes
- Abrufen der Parameter aus dem Skriptwerkzeug
- Importieren der DLL in den Speicher
- Abrufen eines Zeiger auf die Funktion in der DLL
- Angeben der erforderlichen Funktionsargumenttypen, die aus DLLs exportiert wurden, durch Festlegen des argtypes-Attributs und des Rückgabetyps
- Übergeben der Parameter an den C++-Code in der DLL
Details
Das C++-Projekt ("GPToolAsSimpleDLL" in diesem Beispiel) ist ein einfaches Win32-Projekt, in dem der Feature-Class ein AREA-Feld hinzugefügt und der Wert berechnet wird.
Die Header-Datei
#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*);
Die Quelldatei "GPToolAsSimpleDLL" öffnet die angegebene Polygon-Feature-Class und legt das angegebene Feld auf die Fläche der einzelnen Polygon-Features fest. Alle Geoverarbeitungsfunktion, die Sie schreiben, können mit einem einfachen Einstiegspunkt für eine C-Funktion implementiert werden, alle in der gleichen DLL, sowie mit Zusätzen für Skriptwerkzeuge, die die einzelnen Funktionen für ArcToolbox verfügbar machen.
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(>);
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;
}
Das Python-Skript fungiert als Broker. Es akzeptiert die beiden Parameter vom Skriptwerkzeug als Text und sendet sie als nullterminierte char *-Zeichenfolgen an die DLL-Funktion. Bevor die Funktion in der DLL aufgerufen wird, verwendet das Skript auch das Geoverarbeitungswerkzeug "AddField". Dies kann den Workflow robuster machen, indem die proprietären Funktionen in C++ und die gängigen Tasks in Python implementiert werden.
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")