Ctypes, вызывающий внешнюю функцию windll
Я хочу вызвать некоторые функции из внешней библиотеки DLL, используя Python3 на Windows. Библиотека и функции, которые я хочу использовать, как показано ниже;
MECAB_DLL_EXTERN mecab_t* mecab_new2(const char *arg);
MECAB_DLL_EXTERN const char* mecab_sparse_tostr(mecab_t *mecab, const char *str);
MECAB_DLL_EXTERN void mecab_destroy(mecab_t *mecab);
Мне нужно позвонить mecab_new2
во-первых, получить указатель из его возврата и использовать его на mecab_sparse_tostr
затем, наконец, утилизируйте его, используя тот же указатель, вызвав mecab_destroy
,
Я обнаружил, что в C# работает следующее (если это помогает в качестве ссылки):
[DllImport(@"C:\libmecab.dll", CallingConvention = CallingConvention.Cdecl)]
private extern static IntPtr mecab_new2(string arg);
[DllImport(@"C:\libmecab.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
private extern static IntPtr mecab_sparse_tostr(IntPtr m, byte[] str);
...
{
IntPtr mecab = mecab_new2("-Owakati"); // returns a pointer
mecab_sparse_tostr(mecab, Encoding.UTF8.GetBytes(input));
Но не может сработать аналогичным образом в Python. Я пробовал следующее с различными типами рестайлинга и argtypes. Но mecab_new2
Функция всегда возвращает 0 (я предполагаю, что его ноль?).
import ctypes
mecab_dll = ctypes.WinDLL(r"C:\libmecab.dll")
mecab_new2 = mecab_dll['mecab_new2']
mecab_new2.restype = ctypes.POINTER(ctypes.c_int)
mecab_new2.argtypes = [ctypes.c_char_p]
p1 = ctypes.c_char_p(b"-Owakati")
res = mecab_new2(p1)
print(res.contents)
# ValueError: NULL pointer access
Если я удаляю аргумент restype, он возвращает 0, с restype = ctypes.POINTER(ctypes.c_int)
возвращает нулевой указатель.
Я просмотрел похожие вопросы и документацию, но не смог найти как. Я очень плох с C++ и, следовательно, с ctypes.
Благодарю.
РЕДАКТИРОВАТЬ: я пробовал другую функцию из библиотеки, которая не нуждается в аргументах, и она сработала правильно. Итак, я предполагаю, что моя проблема состоит из аргументов, не совпадающих? или библиотека как-то сломана?
Заголовочный файл:
MECAB_DLL_EXTERN const char* mecab_version();
Код Python:
mecab_ver = mecab_dll["mecab_version"]
mecab_ver.restype = ctypes.c_char_p
print(mecab_ver()) # returns b'0.996' which is correct
1 ответ
Я думаю, что ваша проблема может быть здесь:
mecab_dll = ctypes.WinDLL(r"C:\libmecab.dll")
WinDLL означает использование соглашения о вызовах Windows DLL (stdcall). Однако в C# вы используете соглашение о вызовах C (cdecl):
[DllImport(@"C:\libmecab.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
Если ваш код C# работает, попробуйте переписать ваш вызов ctypes следующим образом:
mecab_dll = ctypes.cdll.LoadLibrary(r"C:\libmecab.dll")
Изменить: Вы также делаете немало работы, чтобы передать эту строку в вашу функцию. Вы должны иметь возможность просто сделать это (я не уверен на 100%, что это будет работать в Python3 - это работает безупречно в Python2):
mecab_dll = ctypes.cdll(r"C:\libmecab.dll")
res = mcab_dll.mecab_new2(b"-Owakati")
Python хорошо разбирается в определении типов во внешних функциях - вам не нужно объявлять их, если вы не делаете что-то необычное.
Редактировать 2 Это работает для меня, используя Python 2, 32-битный: я делаю это из интерактивного приглашения. Рабочий каталог C:\Program Files (x86)\MeCab\bin
mecab = ctypes.cdll.LoadLibrary("libmecab.dll")
res = mecab.mecab_new2("-Owakati")
res
затем является ненулевым целым числом (представляется допустимым указателем).