スクリプト ツールからの DLL 呼び出し
ctypes は Python バージョン 2.5 で新規に追加された外部関数ライブラリです。ctypes モジュールは C 互換のデータ タイプを提供し、DLL または共有ライブラリ内の関数の呼び出しを可能にします。Python の ctypes モジュールを使用すると、C++ で記述された ArcObjects コードをジオプロセシング スクリプト ツールで使用できるようになります。
Python の ctypes を使用すると、C++ コードを再コンパイルする必要なく、スクリプト ツールで想定されるパラメータおよびタイプを簡単に変更することができます。ctypes モジュールは、char、int、float、double などの基本のデータ タイプを持つ、C 呼び出しが可能なすべての関数、および構造体とポインタをサポートしています。Python 2.6.2 の ctypes モジュールの詳細については、「16.15 ctypes - A foreign function library for Python(16.15 ctypes - Python の外部関数ライブラリ)」をご参照ください。
Python スクリプトからの DLL 呼び出しには多くのメリットがあります。細分化された ArcObjects クラスをジオプロセシング タスクに利用でき、知的所有権が保護されます。また、IGPFunction2 インタフェースや IGPFunctionFactory インタフェースを使用しなければならない場合と比べて実装がはるかに容易になっています。オブジェクトを Python で作成し、実行方法を呼び出すと、ジオプロセシング フレームワークを使用するスクリプトから必須パラメータが渡されます。
機能について
使用手順は次のとおりです。
Visual Studio 2008 で、プロトタイプを示す単純な関数をエクスポートする C++ Win32 プロジェクトを作成します。
int GpExecutetool(char* parameter1, char* parameter2)
必ず ArcGIS\com\directory をプロジェクトに含め、ArcObjects *.olb ファイルをインポートしてください。
2 つのパラメータを整合チェックしてスクリプトに渡すスクリプト ツールをカスタム ツールボックス内に作成します。
作成した Python スクリプトは次のことを実行します。
- arcpy と ctypes をインポート
- スクリプト ツールからパラメータを取得
- DLL をメモリへインポート
- DLL 内の関数へのポインタを取得
- argtypes 属性およびリターンのタイプを設定して、DLL からエクスポートされる関数の必須の引数タイプを指定
- パラメータを DLL 内の C++ コードに渡す
詳細情報
この C++ プロジェクト(この例では GPToolAsSimpleDLL)は、AREA フィールドをフィーチャクラスへ追加して値を計算する単純な Win32 プロジェクトです。
ヘッダー ファイル
#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 ソース ファイルは、指定されたポリゴン フィーチャクラスを開始し、指定のフィールドを各ポリゴン フィーチャのエリアに設定します。記述したすべてのジオプロセシング関数は、すべてが同じ DLL 内にある単純な C 関数エントリ ポイントと、各関数を 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;
}
Python スクリプトはブローカーの役割を果たし、スクリプト ツールから 2 つのパラメータをテキストとして受け取り、それらをゼロ終端 char* 文字列として DLL 関数に送出します。また、このスクリプトは、DLL 内の関数呼び出しの前に AddField ジオプロセシング ツールを使用します。これによって、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")