Вызов DLL из инструмента-скрипта

Новое в Python версии 2.5 это - ctypes, библиотека внешний функций. Она обеспечивает C-совместимые типы данных и позволяет вызывать функции с помощью динамически-подключаемых библиотек (DLL) или других общих библиотек. Используя модуль ctypes в Python, в инструменте-скрипте геообработки можно использовать код ArcObjects, написанный в C++ .

Применяя ctypes в Python, вы можете с легкостью изменять параметры и типы инструмента-скрипта без перекомпиляции кода C++. Модуль ctypes поддерживает любую C- функцию с основными типами данных, например char, int, float и double а также structs и pointers. Более подробно о модуле ctypes в Python 2.6.2 см. 16.15 ctypes—Библиотека внешних функций (A foreign function library) для Python.

Возможность вызова DLL из скрипта Python дает множество преимуществ. Вы можете использовать с выгодой для себя тонко настроенные классы ArcObjects в ваших заданиях по геообработке, ваша интеллектуальная собственность будут защищена, и это намного проще сделать, чем пользоваться интерфейсами IGPFunction2 и IGPFunctionFactory. Вы создаете ваш объект в Python и вызываете метод выполнения (execute), передавая необходимые параметры из скрипта при помощи структуры геокодирования.

Как это работает

Необходимо выполнить следующие действия:

Создание проекта C++ Win32 в Visual Studio 2008, поддерживающего экспорт простой функции с прототипом:

int GpExecutetool(char* parameter1, char* parameter2)

Не забудьте включить в проект директорию ArcGIS\com\directory и импортировать файлы ArcObjects .olb .

Создайте инструмент-скрипт в пользовательском наборе инструментов, где производится проверка этих двух параметров и их переотправка в скрипт.

Ваш скрипт Python сделает следующее:

Подробности

Проект C++ project (именуемый для данного примера GPToolAsSimpleDLL) является простым проектом Win32, который добавляет в класс пространственных объектов поле AREA и вычисляет его значение.

Файл заголовка

#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*);

Файл – источник GPToolAsSimpleDLL открывает указанный класс полигональных объектов и устанавливает указанное поле в область (area) каждого полигонального объекта. Каждая функция геообработки, которую вы пишете, может быть реализована простой точкой входа функции С (C function), все в одном и том же DLL, вместе с компаньонами инструмента-скрипта, чтобы каждая функция была видна в 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;
}

Скрипт Python действует как брокер, принимая два параметра от инструмента-скрипта в текстовом виде и пересылая их в функцию DLL какchar*, zero-terminated strings. До вызова функции в DLL данный скрипт использует также инструмент геообработки Add Field. Это может сделать ваш рабочий процесс более сложным, требующим реализации вашей персональной функциональности в C++ в сочетании с выполнением общих задач в 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")
9/10/2013