MRが楽しい

MRやVRについて学習したことを書き残す

PythonのctypesでDLLの関数から様々な戻り値を受け取る その1

本日は Python の技術調査枠です。
Python の ctypes で DLL の関数から様々な戻り値を受け取る方法について記事にします。
f:id:bluebirdofoz:20191117232312j:plain

前回記事の続きです。
bluebirdofoz.hatenablog.com

ctypesの基本データ型

ctypes では以下の C 互換なデータ型が定義されています。

ctypes の型C の型Python の型
c_bool_Boolbool (1)
c_charchar1文字のバイト列オブジェクト
c_wcharwchar_t1文字の文字列
c_bytecharint
c_ubyteunsigned charint
c_shortshortint
c_ushortunsigned shortint
c_intintint
c_uintunsigned intint
c_longlongint
c_ulongunsigned longint
c_longlong__int64 または long longint
c_ulonglongunsigned __int64 または unsigned long longint
c_size_tsize_tint
c_ssize_tssize_t または Py_ssize_tint
c_floatfloat浮動小数点数
c_doubledouble浮動小数点数
c_longdoublelong double浮動小数点数
c_char_pchar * (NUL 終端)バイト列オブジェクトまたは None
c_wchar_pwchar_t * (NUL 終端)文字列または None
c_void_pvoid *整数または None

以下の通り、型を呼び出すことで利用します。
値を取り出す場合は .value を呼び出します。

import ctypes

x = c_int(123)
print(x)
print(x.value)

f:id:bluebirdofoz:20191117232322j:plain

引数に整数型を指定する

試しに前回のプロジェクトを修正し、DLL に以下の long 型を扱う関数を追加します。
・TestLib.h

#pragma once

#ifdef TESTLIB_EXPORTS
#define TESTLIB_API extern "C" __declspec(dllexport)
#else
#define TESTLIB_API extern "C" __declspec(dllimport)
#endif

TESTLIB_API int SumInt(int, int);
TESTLIB_API long SumLong(long, long);

・TestLib.cpp

#include "stdafx.h"

#include "TestLib.h"

int SumInt(int x, int y)
{
    return x + y;
}

long SumLong(long x, long y)
{
    return x + y;
}

次に関数を呼び出す Python スクリプトを修正します。
以下の通り、c_long 型で変数を定義して引数を渡す処理を記述します。
・dllAccesstest02.py

# ctypesインポート
from ctypes import *
# DLLをロードする
dll = cdll.LoadLibrary("TestLib.dll")

# long 型の変数を作成する
long_x = c_long(100)
long_y = c_long(200)
# SumInt関数を呼び出して結果を出力する
print("SumLong Execute")
print(dll.SumLong(long_x, long_y))
print(dll.SumLong(long_x.value, long_y.value))

関数を呼び出すことができ、戻り値の 300 が確認できました。
スクリプトの通り、変数をそのまま受け渡しても .value で受け渡しても正常に動作します。
f:id:bluebirdofoz:20191117232333j:plain

戻り値の型を取得する

次に戻り値を変数で取得し、その型を確認してみます。

# ctypesインポート
from ctypes import *
# DLLをロードする
dll = cdll.LoadLibrary("TestLib.dll")

# long 型の変数を作成する
long_x = c_long(100)
long_y = c_long(200)
# 戻り値を変数で取得する
long_sum = dll.SumLong(long_x, long_y)
# 型と値を確認する
print("SumLong Execute")
print(type(long_sum))
print(long_sum)

すると以下の通り、戻り値は c_long 型ではなく int 型で取得されていることが分かります。

SumLong Execute
<class 'int'>
300

f:id:bluebirdofoz:20191117232343j:plain

関数の戻り値の型をスクリプト中で確認したい場合は関数の .restype の参照を利用することで可能です。
・dllAccessTest03.py

# ctypesインポート
from ctypes import *
# DLLをロードする
dll = cdll.LoadLibrary("TestLib.dll")
# 関数の戻り値の型を確認する
print(dll.SumLong.restype)

以下の通り、c_long の戻り値の型が示されます。

<class 'ctypes.c_long'>

f:id:bluebirdofoz:20191117232351j:plain

参考ページ

docs.python.org