首页 > 解决方案 > 动态加载的 DLL 导致 Python 初始化失败

问题描述

我和同事一起尝试通过 Cython 将 Python 文件转换为 C 文件。之后,我们尝试从 C 文件中制作一个 DLL。但是,这不起作用,Python 文件的初始化失败。

为了完整起见,这里是我的代码和我执行的步骤:

Python 文件:hello.pyx

cdef public int sayHello ():
    print("hello out there")
    cdef int a = 5
    return a

为了从 hello.pyx 创建一个 C 文件,我们在终端中使用了以下命令:

Cython -3 hello.pyx

然后创建一个 hello.h 文件和一个 hello.c 文件。hello.c 文件包含大量代码,我是 C 编程的非常非常初学者。因此,我并不完全了解其中发生的一切。我的同事支持我对以下文件进行更改。

为了使这些方法可以在 DLL 中访问,我们在最后的hello.c文件中添加:

__declspec(dllexport) int Py_sayHello()
{
    return __Pyx_PyInt_As_int(sayHello());
}

并通过以下方式替换了hello.h文件中的方法:

#ifndef __PYX_HAVE__hello
#define __PYX_HAVE__hello

#include "Python.h"

extern "C" 
{
    __declspec(dllexport) int  Py_sayHello();
    __declspec(dllexport) void Py_init();
}


#endif /* !__PYX_HAVE__hello */

我们将 hello.c 文件中的第 203 行更改为:

enum { __pyx_check_sizeof_voidp = 1 / (int)(SIZEOF_VOID_P == sizeof(void*)) };

enum { __pyx_check_sizeof_voidp = (int)(SIZEOF_VOID_P == sizeof(void*)) };

修复错误。

之后我们执行以下两个命令来创建 dll。

gcc -c -DBUILD_DLL hello.c -IC:/Users/hannah/Anaconda3/include
gcc -shared -o hello.dll hello.o -LC:/Users/hannah/Anaconda3/libs -lpyhton38

这成功地创建了 hello.dll。如果我用 Dependency Walker 打开这个 DLL,我看到有 2 种方法可用: 依赖 Walker 中的方法

当我们尝试通过 DLL 调用函数时,程序在 Python 文件的初始化处停止。要调用函数,我们使用以下代码:

#ifndef DLLHANDLER_C_
#define DLLHANDLER_C_

#include <windows.h>
#include <winbase.h>
#include <windef.h>
#include <stdio.h>

typedef int (*pFunHello)();
typedef void (*pFunInit)(void);

int loadDLL();

int main(){
    //int x = 100;
    //int x = loadDLL();
    loadDLL();
}

int loadDLL( ) {
    int status = 10;
    pFunHello _TestFunc;
    pFunInit _Py_init;

    HMODULE testLibrary = LoadLibrary("hello.dll");
    if (testLibrary != NULL)
    {
        printf("Library successfully loaded.\n");

        _Py_init = (pFunInit)GetProcAddress(testLibrary, "PyInit_hello");
        if (_Py_init != NULL)
        {
            printf("PyInit_hello successfully cached.\n");
            _Py_init();
            printf("Python initialization successful.\n");
            
            _TestFunc = (pFunHello)GetProcAddress(testLibrary, "Py_sayHello");
            if (_TestFunc != NULL)
            {
                printf("Py_sayHello successfully cached.\n");

                status = _TestFunc();
                printf("Py_sayHello returned %d\n", status);
            }
            else 
            {
                printf("Error when caching Py_sayHello: %d\n", GetLastError());
            }
        }
        else 
        {
            printf("Error when caching PyInit_hello: %d\n", GetLastError());
        }
        
        FreeLibrary(testLibrary);
    }
    else 
    {
        printf("Error occurred when loading library: %d\n", GetLastError());
    }

    return status;
}

#endif

输出是:

Library successfully loaded.
PyInit_hello successfully cached.
Calling PyModuleDef_Init...

在命令行中通过命令:

echo %Error Level%

出现错误代码-1073741819,表示“访问冲突错误”。我的同事能够确定,错误来自 python38.dll 中的 PyModuleDef_Init。

希望你们中的一些人可以帮助我们解决这个问题。有人对cython有很多经验吗?我们如何修复这个错误并使 hello.dll 正常工作?

标签: pythondllcython

解决方案


推荐阅读