Ctypes,调用外部windll函数

时间:2017-04-18 02:02:49

标签: python ctypes mecab

我想在Windows上使用Python3从外部dll调用一些函数。我想要使​​用的库和函数如下:

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中无法解决类似的问题。我尝试了以下不同的restypes和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 个答案:

答案 0 :(得分:2)

我认为您的问题可能就在这里:

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是一个非零整数(似乎是一个有效的指针)。