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")