Appel d'une DLL à partir d'un outil de script
ctypes, une bibliothèque de fonctions étrangère, est une nouveauté de la version 2.5 de Python. Elle fournit des types de données compatibles avec le langage C et permet d'appeler des fonctions qui résident dans des fichiers DLL ou des bibliothèques partagées. Le module ctypes de Python permet d'utiliser du code ArcObjects écrit dans C++ dans un outil de script de géotraitement.
Le module ctypes de Python vous permet de modifier facilement les paramètres et les types que l'outil de script attend sans devoir recompiler notre code C++. Le module ctypes prend en charge toutes les fonctions pouvant être appelées avec le langage C avec des types de données de base, tels que char, int, float et double ainsi que des structures et des pointeurs. Pour plus d'informations sur le module ctypes de Python 2.6.2, reportez-vous à la rubrique ctypes 16.15 ctypes - Une bibliothèque de fonctions étrangère pour Python.
La possibilité d'appeler une DLL à partir d'un script Python présente de nombreux avantages. Vous pouvez tirer parti des classes d'objet ArcObjects détaillées dans vos tâches de géotraitement : votre propriété intellectuelle est protégée. En outre, il beaucoup plus facile de les implémenter que d'utiliser des interfaces IGPFunction2 et IGPFunctionFactory. Vous créez votre objet dans Python et appelez la méthode Execute, pour transférer les paramètres requis à partir du script dans la structure de géotraitement.
Fonctionnement
La procédure est la suivante :
Créez un projet de C++ Win32 dans Visual Studio 2008 chargé d'exporter une fonction simple avec le prototype :
int GpExecutetool(char* parameter1, char* parameter2)
Veillez à inclure le répertoire ArcGIS\com\ dans le projet et importez les fichiers ArcObjects .olb.
Créez un outil de script dans une boîte à outils personnalisée qui valide les deux paramètres et les transmet au script.
Votre script Python effectuera les opérations suivantes :
- Importer arcpy et ctypes.
- Obtenir les paramètres de l'outil de script.
- Importer la DLL dans la mémoire.
- Orienter un pointeur vers la fonction dans la DLL.
- Spécifier les types d'argument requis de fonctions exportées depuis les DLL en définissant l'attribut argtypes ainsi que le type de retour.
- Transférer les paramètres vers le code C++ de la DLL.
Détails
Le projet C++ (nommé GPToolAsSimpleDLL dans cet exemple) est un projet Win32 simple qui ajoute un champ AREA à la classe d'entités et calcule la valeur.
Le fichier d'en-tête
#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*);
Le fichier source GPToolAsSimpleDLL ouvre la classe d'entités surfaciques spécifiée et définit le champ spécifié sur la surface de chaque entité surfacique. Chaque fonction de géotraitement que vous programmez peut être implémentée avec un point d'entrée de fonction C simple, dans la même DLL, avec des outils de script complémentaires chargés d'exposer chaque fonction auprès d'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(>); 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; }
Le script Python joue le rôle d'intermédiaire : il accepte en entrée les deux paramètres de l'outil de script sous forme de texte et les transmet à la fonction DLL en tant que chaînes char* qui se terminent par un zéro. Le script utilise également l'outil de géotraitement AddField avant que la fonction ne soit appelée dans la DLL. Le fait d'implémenter votre fonctionnalité dans C++ et les tâches courantes dans Python présente l'avantage de pouvoir rendre votre workflow plus robuste.
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")