如何启用pyd来调用外部C ++ DLL中的函数?

时间:2017-07-06 16:02:55

标签: python visual-studio python-c-api python-c-extension

我试图为我的python程序设计一个外部DLL。现在我可以使用 C ++ Visual Studio 2010 生成一个后缀为".pyd"的文件。如果.pyd没有附加C ++生成的其他.dll文件,那么这个python库可以很好地工作。

但是,现在我需要设计一个带有这样结构的.pyd文件:

A.pyd -> 1.dll -> 2.dll

其中文件1,2是C库。生成A.pyd时会调用这些库中的函数。

虽然 VS 2010 可以生成这个.pyd文件而没有错误,但它无法在python 3.6中运行。错误报告如下:

Traceback (most recent call last): File "<stdin>", line 1, in <module> ImportError: DLL load failed, could not find the target program.

即使引用的<stdin><module>存储在包含1.dll 的同一文件夹中,此错误仍然存​​在。我想知道如何启用 python-C ++ 库来调用基于2.dll 的动态链接库中的函数。

1 个答案:

答案 0 :(得分:0)

OK!现在我找到了执行此操作的正确方法!

包装DLL

首先,如果您需要调用的dll (我称之为dll-A尚未导出任何功能(您可以在VS命令中使用dumpbin来检查导出的函数),你需要包装原始的dll-A。使用隐式调用包含dll-A。如果在原始的.h / .lib中声明了一个函数,就像这样:

int func(int a, int b);

然后你需要创建一个新的dll项目并用这个包装上面的函数:

·H

extern "C" _declspec(dllexport) int w_func(int a, int b);

的.cpp

int w_func(int a, int b){
    return func(a, b);
}

当然,如果dumpbin显示dll-A具有可用的导出功能,则可以跳过此步骤。

导出此dll (我称之为dll-B后,您将获得&#39; dll-B&#39;及其依赖文件(包括dll-Adll-A依赖文件。)

编写.pyd文件

使用显式调用来引用dll-B。使用此方法时,不应包含任何lib / .h文件,因为dll-B本身可以为您提供足够的接口。您可以通过以下方法加载dll:

·H

typedef int (*func_ptr)(int a, int b);

.cpp(编写.pyd项目时名为Testdll的函数的一部分)

func_ptr FUNC_API = NULL;
HINSTANCE h = LoadLibraryA("libdll/dllB.dll"); 
//Certainly! you could set the folder of stored dlls!
if (h){
    FUNC_API = (func_ptr)GetProcAddress(h, "w_func");
    //You could load more functions here.
}
else{ // If the dll could not be found
    FreeLibrary(h);
    return 0x100;
}
int errorflag=0;
if (FUNC_API==NULL){ //Check whether the function is valid.
    cout << "Could not find: func" << endl;
    errorflag = errorflag | 0x001;
}
//You could check more functions here.
if (errorflag!=0){ // if any function could not be found.
    FreeLibrary(h);
    return 0x100 | errorflag;
}
//process functions.
int a,b,c;
c = FUNC_API(a,b);
//Free the lib
if (h)
    FreeLibrary(h);

在构建自己的.pyd之后,你可以得到你的python数据库(我称之为pyd-C

将.pyd链接到Python

在python项目中,您可以通过以下方法测试此文件:

的.py

import pydC

if __name__ == '__main__':
    X = pydC.cclass()
    X.Testdll();

然后你会发现这个功能表现得很好。

注意你的.pyd应该与.py所在的文件夹在同一个文件夹中。由于您已在dll-B中设置了libdll/dllB.dll,因此dll-B应放在名为libdll的文件夹中。但是,由于dll-B隐式调用dll-A和其他依赖dll,dll-A和其他文件应位于工作区文件夹中,即与.py所在的文件夹相同。

简而言之,您需要输入工作区文件夹,文件夹构成如下:

./
    Test.py
    pydC.pyd
    dllA.dll
    dllA_depended1.dll
    ...
    libdll/
        dllB.dll