Invocar una DLL desde una herramienta de secuencia de comandos
Lo nuevo de la versión 2.5 de Python es el ctypes, una biblioteca de funciones externas. Este proporciona tipos de datos compatibles con C y permite invocar funciones de las DLL o bibliotecas compartidas. La utilización del módulo ctypes de Python permite que el código de ArcObjects escrito en C++ se utilice en una herramienta de secuencia de comandos de geoprocesamiento.
Utilizar ctypes de Python le permite cambiar fácilmente los parámetros y los tipos que espera la herramienta de secuencia de comandos sin tener que recompilar el código de C++. El módulo ctypes es compatible con cualquier función con tipos de datos básicos que se pueda invocar a través de C, tales como car, entero, flotante y doble, así como también estructuras y punteros. Para obtener más información acerca del módulo ctypes de Python 2.6.2, consulte16.15 ctypes—Una biblioteca de funciones externas para Python.
Invocar una DLL desde una secuencia de comandos de Python otorga muchos beneficios. Puede aprovechar las clases detalladas de ArcObjects en las tareas de geoprocesamiento, la propiedad intelectual está protegida y es mucho más fácil de implementar que al utilizar las interfaces de IGPFunction2 y IGPFunctionFactory. Crea el objeto en Python e invoca el método de ejecución, introduce los parámetros requeridos de la secuencia de comandos mediante el marco de geoprocesamiento.
Funcionamiento
Los pasos son los siguientes:
Cree un proyecto Win32 de C++ en Visual Studio 2008 que exporte una función simple con el prototipo:
int GpExecutetool(char* parameter1, char* parameter2)
Asegúrese de incluir el ArcGIS\com\directory en el proyecto y de importar los archivos .olb de ArcObjects.
Cree una herramienta de secuencia de comandos en una caja de herramientas personalizada que valide los dos parámetros y que los introduzca en la secuencia de comandos.
La secuencia de comandos de Python hace lo siguiente:
- Importa arcpy y ctypes.
- Obtiene los parámetros de la herramienta de secuencia de comandos.
- Importa la DLL a la memoria.
- Obtiene un puntero para la función de la DLL.
- Especifica los tipos de funciones de argumento requerido que se exportan desde las DLL al establecer el atributo de argtypes y el tipo de devolución.
- Introduce los parámetros del código de C++ en la DLL.
Los detalles
El proyecto de C++ (denominado GPToolAsSimpleDLL para este ejemplo) es un proyecto Win32 simple que agrega un campo AREA a la clase de entidad y calcula el valor.
El archivo de encabezado
#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*);
El archivo de origen GPToolAsSimpleDLL abre la clase de entidad poligonal especificada y establece el archivo especificado en el área de cada entidad de polígono. Cada función de geoprocesamiento que escribió se puede implementar con un punto de entrada de función de C simple, en la misma DLL, junto con compañeros de la herramienta de secuencia de comandos, para exponer cada función en 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; }
La secuencia de comandos de Python actúa como mediador al aceptar los dos parámetros de la herramienta de secuencia de comandos como texto y enviarlos a la función de la DLL como cadenas de texto car* terminadas en cero. La secuencia de comandos también utiliza la herramienta de geoprocesamiento Añadir campo antes de que se invoque la función de la DLL. Esto puede hacer que el flujo de trabajo sea más sólido al implementar la funcionalidad propietaria de C++ y las tareas comunes de 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")