Pybind11:如何真正解决缺失DLL错误?

0 投票
1 回答
58 浏览
提问于 2025-04-14 17:33

我在学习Pybind11的时候,做了一个简单的项目,但像往常一样,出了点问题。当我试图在Python Shell或文件中导入这个模块时,遇到了一个错误。

ImportError: DLL加载失败,导入test_module时找不到指定的模块。

这个pybind模块里有一个函数可以打印“Hello World”。

#include <iostream>
#include <pybind11/pybind11.h>


void sayHello() {
  std::cout << "Hello World!\n"; 
}


PYBIND11_MODULE(test_module, m) {
  m.doc() = "This is momumental!";
  m.def("sayHello", &sayHello, "Function that prints Hello World");
}

CMake文件也没有问题。

cmake_minimum_required(VERSION 3.10)
project("test_module")

set(EXECUTABLE_OUTPUT_PATH "../")
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
add_subdirectory(".extern/pybind11")

pybind11_add_module("test_module" main.cpp)
target_compile_options("test_module" PRIVATE "-static-libgcc" "-static-libstdc++")

当时我不知道该怎么办,心里很沮丧,因为又遇到了一个“黑箱”问题。我把这个项目放到了github上,如果你想更详细地看看代码是怎么结构的,可以去这里看看。https://github.com/ProarchwasTaken/pybind_test

我尝试过的。

经过一番愤怒的网上搜索,我找到了一种解决问题的方法。多亏了pybind库中的这个讨论串:https://github.com/pybind/pybind11/issues/2010

从讨论中看,错误的意思是我的pybind模块需要的一些DLL文件缺失,而解决办法是添加缺失DLL的路径。

>>> import os
>>> os.add_dll_directory("C:\\MinGW\\bin") # The path where DLLs the customized pybind module needs are locating.
<AddedDllDirectory('C:\\MinGW\\bin')>
>>> import example
>>> example.add(1, 2)
3

我在Python Shell中尝试这样做,结果成功了……我又玩了一会儿,设置了一个小包,里面有一个init.py文件,并创建了一个存根文件,这样我的LSP和类型检查器就知道这个函数存在,不会报错。

主要问题。

虽然这个解决方案有效,但还有一个问题。它并不真正可移植。我的DLL目录在:“C:/msys64/mingw64/bin”,但并不是每个人的DLL目录都在同一个地方,更不用说是否有mingw64了。

这个模块只适用于我的电脑。如果我想把我做的东西分享出去呢?如果其他人想用这个模块,他们就得自己修改代码,或者下载一些他们不想要的东西。

我能想到的解决办法就是找到模块缺失的DLL文件,并将它们静态链接到模块的二进制文件中。这样,大家就不用再处理add_dll_directory的麻烦了。

我尝试用依赖分析工具找出可能缺失的DLL,但我找到的唯一线索是:“ext-ms-win-oobe-query-l1-1-0.dll”。不过我觉得这个线索不太相关,因为当我在test.py中添加了DLL目录后,模块可以正常导入。所以我只能推测,模块缺失的DLL就在那个目录里。

正如我所说,我很沮丧,似乎只有我一个人遇到这个问题。我在想,是否有人能帮我解决这个问题。

1 个回答

0

大家好,我找到解决我问题的方法了!原来,模块需要的那个DLL文件是“libwinpthread-1.dll”。知道这一点后,我只需要在cmake文件中把它静态链接上就行了。不过,为了保险起见,你可能还想把“static-libgcc”和“static-libstdc++”也链接上。

target_link_libraries("test_module" PRIVATE -static -lpthread -static-libgcc -static-libstdc++)

我这样做之后,编译了模块文件,就可以直接导入这个模块了,不用再用“os.add_dll_directory”了!终于可以继续做其他事情了!

撰写回答