【问题标题】:How do I call a windows dll function from cygwin python?如何从 cygwin python 调用 windows dll 函数?
【发布时间】:2019-03-03 05:37:19
【问题描述】:

我有一个Windows python3.7 函数,它使用ctypes 成功调用kernel32.dll GetSystemPowerStatus 函数来询问电源状态,以查看我的笔记本电脑是使用交流电源还是电池供电。这是一个纯python解决方案。

我想将此功能移植到cygwin python3.7。开箱即用,python3cygwinctypes 似乎不允许调用 Windows dll。我更喜欢纯 Python 解决方案,但如有必要,我可以使用 C/C++。有没有人有如何做到这一点的例子?

编辑添加代码(第 63-67 行)和错误消息:

elif _os.name == 'posix' and _sys.platform == 'cygwin':
    # c:\Windows\System32\kernel32.dll
    kernel32_name = '/proc/cygdrive/c/Windows/System32/kernel32.dll'
    kernel32 = CDLL(kernel32_name)
    _GetSystemPowerStatus = kernel32.GetSystemPowerStatus


$ python3.7 GetSystemPowerStatus.py
Traceback (most recent call last):
  File "GetSystemPowerStatus.py", line 82, in <module>
    result, systemPowerStatus = GetSystemPowerStatus()
  File "GetSystemPowerStatus.py", line 66, in GetSystemPowerStatus
    kernel32 = CDLL(kernel32_name)
  File "/usr/lib/python3.7/ctypes/__init__.py", line 356, in __init__
    self._handle = _dlopen(self._name, mode)
OSError: Invalid argument

python2.7 给出了同样的错误,但在第 366 行。

解决了。请参阅下面我自己的答案。

【问题讨论】:

  • 请给出您在 cygwin 中运行工作 Windows 代码时收到的错误消息
  • '$ python3.7 GetSystemPowerStatus.py⏎ Traceback(最近一次调用最后):⏎文件“GetSystemPowerStatus.py”,第69行,在⏎结果中,systemPowerStatus = GetSystemPowerStatus()⏎文件“GetSystemPowerStatus.py”,第 57 行,在 GetSystemPowerStatus⏎ GetSystemPowerStatus = cdll.kernel32.GetSystemPowerStatus⏎ 文件“/usr/lib/python3.7/ctypes/__init_.py”,第 426 行,在 getattr__⏎ dll = self._dlltype(name)⏎ 文件“/usr/lib/python3.7/ctypes/__init.py”,第 356 行,在 __init__⏎ self._handle = _dlopen(self._name , mode)⏎ OSError: No such file or directory⏎'
  • 使用其他格式化信息编辑问题。评论显然是一团糟。
  • 非常令人费解,实际上这对我有用:ctypes.CDLL("/cygdrive/c/windows/system32/user32.dll") 但不适用于kernel32.dll。我得到完全相同的错误。所以可以从 cygwin 加载一个 windows 库,但是 not kernel32 :(
  • 谢谢。很有意思。我猜必须挖掘 ctypes 的来源。

标签: python windows dll cygwin ctypes


【解决方案1】:

正如你一样,我无法获得kernel32.dll(尽管它可以与user32msvcrtkernelbase 等其他 DLL 一起使用)

我找到了一种非常人为的方法...它使用 kernelbase.dll(导出 GetModuleHandle)来获取 kernel32.dll 的句柄,然后使用句柄可选关键字调用 CDLL

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
import ctypes

def main():
    # the idea is to load kernelbase.dll which will allow us to call GetModuleHandleW() on kernel32.dll
    try:
        kernel_base = ctypes.CDLL("/cygdrive/c/windows/system32/kernelbase.dll")
    except OSError:
        print("Can't load kernelbase.dll...")
        return -1

    gmhw = kernel_base.GetModuleHandleW
    gmhw.argtypes = (ctypes.c_wchar_p, )
    gmhw.restype = ctypes.c_void_p

    # call GetModuleHandleW on kernel32.dll (which is loaded by default in the Python process)
    kernel32_base_addr = gmhw("kernel32.dll")
    print(f"Got kernel32 base address: {kernel32_base_addr:#x}")

    # now call CDLL with optional arguments
    kernel32 = ctypes.CDLL("/cygdrive/c/windows/system32/kernel32.dll", handle=kernel32_base_addr, use_last_error=True)

    # test with GetSystemPowerStatus
    print(f"GetSystemPowerStatus: {kernel32.GetSystemPowerStatus}")

    return 0

if __name__ == "__main__":
    sys.exit(main())

【讨论】:

  • 谢谢。这有点令人费解。我找到了更好的方法。
【解决方案2】:

在发现我可以轻松加载user32.dll 后,我对源代码和pdbldd 进行了更多调查。以下是我的解决方案。

elif _os.name == 'posix' and _sys.platform == 'cygwin':
    RTLD_LOCAL = 0  # from /usr/include/dlfcn.h
    RTLD_LAZY = 1
    RTLD_NOW = 2
    RTLD_GLOBAL = 4
    RTLD_NODELETE = 8
    RTLD_NOLOAD = 16
    RTLD_DEEPBIND = 32
    kernel32_name = '/proc/cygdrive/c/Windows/System32/kernel32.dll'
    kernel32 = CDLL(kernel32_name, mode=RTLD_LAZY | RTLD_NOLOAD)
    _GetSystemPowerStatus = kernel32.GetSystemPowerStatus

调用函数的常用代码是:

_GetSystemPowerStatus.restype = c_bool
_GetSystemPowerStatus.argtypes = [c_void_p,]

_systemPowerStatus = _SystemPowerStatus()  # not shown, see ctypes module doc

result = _GetSystemPowerStatus(byref(_systemPowerStatus))

return result, SystemPowerStatus(_systemPowerStatus)

这适用于 Python 2.7、3.6 和 3.6

$ uname -a
CYGWIN_NT-10.0 host 3.0.1(0.338/5/3) 2019-02-20 10:19 x86_64 Cygwin

感谢大家的帮助。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2020-02-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多