首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >将ctype函数从Python 2迁移到Python 3

将ctype函数从Python 2迁移到Python 3
EN

Stack Overflow用户
提问于 2021-04-07 06:02:33
回答 1查看 259关注 0票数 0

如果这是XY问题,下面是我想做的事情:

我有一个wxPython应用程序,它必须使用WM_COPYDATA窗口消息与另一个进程通信。虽然用ctypes模块发送消息非常容易,但是接收答案需要我覆盖wx循环,因为wx没有为这种情况提供特定的事件。

在python2上,我使用ctypes.windll.user32.SetWindowLongPtrWctypes.windll.user32.CallWindowProcW函数来获得所需的行为。但是,在python3中,相同的代码会导致OSError: exception: access violation writing

据我所知,python2 ctypes模块和python3 ctypes模块之间唯一的区别是它们如何处理字符串。

我还读到,这两个版本如何布局内存是有区别的,但是由于我不是C专家,所以我的代码中找不到问题。

我已经用python3.7 (64位)和python2.7(64位)和WX4.0.7测试了代码(尽管它也适用于wx2.8和python2)

下面是最小的可重现性示例:

代码语言:javascript
复制
import ctypes, ctypes.wintypes, win32con, wx, sys


_LPARAM = ctypes.wintypes.LPARAM
_WPARAM = ctypes.wintypes.WPARAM
_HWND = ctypes.wintypes.HWND
_UINT = ctypes.wintypes.UINT
_LPCWSTR = ctypes.wintypes.LPCWSTR
_LONG_PTR = ctypes.c_long
_LRESULT = _LONG_PTR
_LPCWSTR = ctypes.wintypes.LPCWSTR

_WNDPROC = ctypes.WINFUNCTYPE(_LPARAM,   # return Value
                              _HWND,     # First Param, the handle
                              _UINT,     # second Param, message id
                              _WPARAM,   # third param, additional message info (depends on message id)
                              _LPARAM,   # fourth param, additional message info (depends on message id)
)


_SetWindowLongPtrW = ctypes.windll.user32.SetWindowLongPtrW
_SetWindowLongPtrW.argtypes = (_HWND, ctypes.c_int, _WNDPROC)
_SetWindowLongPtrW.restypes = _WNDPROC

_CallWindowProc = ctypes.windll.user32.CallWindowProcW
_CallWindowProc.argtypes = (_WNDPROC, _HWND, _UINT, _WPARAM, _LPARAM)
_CallWindowProc.restypes = _LRESULT

def _WndCallback(hwnd, msg, wparam, lparam):
    print(hwnd, msg, wparam, lparam)
    return _CallWindowProc(_old_wndproc, hwnd, msg, _WPARAM(wparam), _LPARAM(lparam))
_mywndproc = _WNDPROC(_WndCallback)


app = wx.App(redirect=False)
frame = wx.Frame(None, title='Simple application')
frame.Show()

_old_wndproc = _WNDPROC( _SetWindowLongPtrW(frame.GetHandle(), win32con.GWL_WNDPROC, _mywndproc ) )
if _old_wndproc == 0:
    print( "Error" )
    sys.exit(1)

app.MainLoop()

编辑:我知道有一个pywin32模块,它可能对我有帮助。但是,由于代码在python2上工作,所以我很好奇这里发生了什么。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-04-07 16:42:33

一个问题是:

代码语言:javascript
复制
_LONG_PTR = ctypes.c_long
_LRESULT = _LONG_PTR

类型LONG_PTR是“指针大小的整数”,它在32位和64位进程之间变化。由于您使用的是64位Python,所以指针是64位的,LONG_PTR应该是:

代码语言:javascript
复制
_LONG_PTR = ctypes.c_longlong

如果您想要32位和64位的更多可移植代码,LPARAM在Windows中也被定义为LONG_PTR,因此下面的定义将正确定义32位和64位Python的LONG_PTR,因为ctypes已经根据ctypes的构建正确地定义了它:

代码语言:javascript
复制
_LONG_PTR = ctypes.wintypes.LPARAM  # or _LPARAM in your case

更改之后,我用wxPython测试了您的脚本,但仍然有问题。我怀疑wxPython是在没有UNICODE/_UNICODE定义的情况下编译的,所以SetWindowLongPtr和CallWindowProc API必须使用A版本来检索和调用旧的窗口过程。我做了这个修改,下面的代码起作用了。

代码语言:javascript
复制
Full code tested with 64-bit Python 3.8.8:
```py

导入ctype,ctypes.wintypes,win32con,wx,sys

_LPARAM = ctypes.wintypes.LPARAM

_WPARAM = ctypes.wintypes.WPARAM

_HWND = ctypes.wintypes.HWND

_UINT = ctypes.wintypes.UINT

_LPCWSTR = ctypes.wintypes.LPCWSTR

_LONG_PTR = _LPARAM

_LRESULT = _LONG_PTR

_LPCWSTR = ctypes.wintypes.LPCWSTR

_WNDPROC = ctypes.WINFUNCTYPE(_LRESULT,#返回值)

代码语言:javascript
复制
                          _HWND,     # First Param, the handle
代码语言:javascript
复制
                          _UINT,     # second Param, message id
代码语言:javascript
复制
                          _WPARAM,   # third param, additional message info (depends on message id)
代码语言:javascript
复制
                          _LPARAM,   # fourth param, additional message info (depends on message id)

)

_SetWindowLongPtr =cypes.winll.user32.SetWindowLongPtrA

_SetWindowLongPtr.argtypes = (_HWND,ctypes.c_int,_WNDPROC)

_SetWindowLongPtr.restypes = _WNDPROC

_CallWindowProc =cypes.winll.user32.CallWindowProcA

_CallWindowProc.argtypes = (_WNDPROC,_HWND,_UINT,_WPARAM,_LPARAM)

_CallWindowProc.restypes = _LRESULT

@_WNDPROC

def _WndCallback(hwnd,msg,wparam,lparam):

代码语言:javascript
复制
print(hwnd, msg, wparam, lparam)
代码语言:javascript
复制
return _CallWindowProc(_old_wndproc, hwnd, msg, wparam, lparam)

app = wx.App(redirect=False)

frame =wx.Frame(无,标题=‘简单应用程序’)

frame.Show()

_old_wndproc = _WNDPROC(_SetWindowLongPtr(frame.GetHandle(),win32con.GWL_WNDPROC,_WndCallback))

如果_old_wndproc == 0:

代码语言:javascript
复制
print( "Error" )
代码语言:javascript
复制
sys.exit(1)

app.MainLoop()

代码语言:javascript
复制

顺便提一下,在SetWindowLongPtr中有一个关于CallWindowProc的注释(与CallWindowProc类似),它暗示了这个解决方案:

H头将SetWindowLongPtr定义为别名,它根据Unicode预处理器常量的定义自动选择此函数的ANSI或UNICODE版本。将编码中性别名的使用与非编码中性的代码混合使用可能会导致不匹配导致编译或运行时错误。有关更多信息,请参见函数原型的约定。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/66980359

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档