从没有此类信息的 pyd 中检索 Python 版本

Retrieve Python version from pyd built without such an information

在 Windows 上,我有一个 Python 库 (*.pyd),我需要检查链接的 Python 版本。可以是 Python 2.7.x 或 3.x.

当我 运行 使用 win32api 这样的东西时(改编自此处:):

def get_pyd_python_version(pyd_path):
    """
    Get the python version required by the passed pyd library
    """
    import win32api
    print("- pyd path: %s" % str(pyd_path))
    try:
        info = win32api.GetFileVersionInfo(pyd_path, '\')
    except win32api.error as e:
        print(str(e.strerror) + " > " + pyd_path)
        return ""

    python_version = "%s.%s.%s.%s" % (
        str(win32api.HIWORD(info['FileVersionMS'])),
        str(win32api.LOWORD(info['FileVersionMS'])),
        str(win32api.HIWORD(info['FileVersionLS'])),
        str(win32api.LOWORD(info['FileVersionLS'])))
    return python_version

我得到:The specified resource type cannot be found in the image file.

相同的函数与其他 "properly" 构建的 pyd 库(例如,win32api.pyd)一起按预期工作。

由于 Dependency Walker 与 incomplete pyd 一起使用正确显示了 DLL,我想知道是否有办法以编程方式(使用 Python)获取版本信息从这样一个事实。

--- 编辑 ---

基于 pefile 库和@vyktor 的回答,我通过下载这个叉子得到了这个工作:https://github.com/BlackXeronic/pefile_py3 并使用这种方法(也检查架构):

from __future__ import absolute_import, print_function
import sys
sys.path.append('./pefile_py3-master')
import pefile

print("python env: v.%s.%s" % (sys.version_info.major, sys.version_info.minor))
print("pefile lib: v.%s" % pefile.__version__)

dll_path = r'C:/Python27/DLLs/bz2.pyd'
print("path to pyd: %s" % dll_path)

pe = pefile.PE(dll_path)
if pe.is_dll():
    for entry in pe.DIRECTORY_ENTRY_IMPORT:
        if "python" in entry.dll:
            if pe.FILE_HEADER.Machine == 0x14C:
                arch = "x86"
            elif pe.FILE_HEADER.Machine == 0x8664:
                arch = "x64"
            else:
                arch = "unknown"
            print(">>> importing %s: v.%s [%s] <<<" % (entry.dll, entry.dll[6:8], arch))

这是我使用 Python 3.4.3 解释器得到的:

python env: v.3.4
pefile lib: v.1.2.10-139
path to pyd: C:/Python27/DLLs/bz2.pyd
>>> importing python27.dll: v.27 [x86] <<<

我在 C:\Python32\DLLs\bz2.pyd 上对此进行了测试,但我没有看到任何 python 的详细信息(文件刚刚重命名为 .dll):

python 版本的唯一相关信息在 IAT 中:

所以你只需要解析 PE 文件并查找导入的 DLL 名称。关于loading IAT from file and I did this using pefile extension (suggested by 的问题很少):

C:\Python27>python.exe
Python 2.7.2 (default, Jun 12 2011, 15:08:59) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import pefile
>>> p = pefile.PE(r'C:\Python32\DLLs\bz2.pyd')
>>> p.is_dll()
True
>>> for entry in p.DIRECTORY_ENTRY_IMPORT:
...     print entry.dll
...
python32.dll
MSVCR90.dll
KERNEL32.dll
>>> p = pefile.PE(r'C:\Python27\DLLs\bz2.pyd')
>>> p.is_dll()
True
>>> for entry in p.DIRECTORY_ENTRY_IMPORT:
...     print entry.dll
...
python27.dll
MSVCR90.dll
KERNEL32.dll

而您只需简单地编写一个查找 pythonXX.dll 字符串的检查。

不幸的是,这只适用于 python 2.7(也许有人可以找到 pefile 的 3.2 分支)。


据我所知,WinAPI 本身不提供在文件中查找导入的简单方法,因此使用嵌入了所有结构的第 3 方扩展是执行此操作的最佳方法。