Search code examples
pythonvisual-studiopython-c-apipython-c-extension

How to enable the pyd to call functions in external C++ dll?


I'm trying to design an external DLL for my python program. Now I could use the C++ and Visual Studio 2010 to produce a file with a postfix of ".pyd". If the .pyd is not attached with other .dll files produced by C++, this python library could work well.

However, now I need to design a .pyd file with such a struction:

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

in which the files 1,2 are C libraries. The functions in these libraries are called when producing A.pyd.

Although this .pyd file could be produced without errors by VS 2010, it could not work in python 3.6. The error report is as follow:

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

Even when the referred 1.dll and 2.dll are stored in the same folder that contains A.pyd, this error still exists. I wonder how could I enable the python-C++ library to call the functions in these dynamic link libraries based on C.


Solution

  • OK! Now I have found the correct way to perform this operation!

    Wrap the DLL

    At first, if the dll (I call it dll-A) that you need to call has not exported any function (you could use dumpbin in VS command to check the exported functions), you need to wrap the original dll-A. Use the implicit call to include dll-A. If there is a function declared in the original .h/.lib, like this:

    int func(int a, int b);
    

    Then you need to create a new dll project and wrap the above function by this:

    .h

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

    .cpp

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

    Certainly, if the dumpbin shows that dll-A have avaliable exported functions, you could skip this step.

    After exporting this dll (I call it dll-B), you will get 'dll-B' and its depending files (including dll-A and dll-A's depending files).

    Write the .pyd file

    Use the explicit call to refer dll-B. When using this method, you should not include any lib/.h files, because dll-B itself could provide you with enough interfaces. You could load the dll by this method:

    .h

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

    .cpp (part of a function named Testdll when you write the .pyd project)

    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);
    

    After build your own .pyd, you could get your python database (I call it pyd-C).

    Link the .pyd with Python

    In python project, you could test this file by this method:

    .py

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

    Then you can find that the function is performed well.

    Noted that your .pyd should be in the same folder with where .py is. Because you have set dll-B in libdll/dllB.dll, dll-B should be put in the folder named libdll. However, because dll-B calls dll-A and other depending dlls implicitly, dll-A and other files should be in your workspace folder, i.e. the same folder with where .py is.

    In short, you need to enter the workspace folder, and the folder formation is as follow:

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