将Python ctypes功能从Python 2迁移到Python 3

将Python ctypes功能从Python 2迁移到Python 3,python,python-3.x,python-2.7,wxpython,ctypes,Python,Python 3.x,Python 2.7,Wxpython,Ctypes,如果这是XY问题,我想做的是: 我有一个wxPython应用程序,它必须使用WM_COPYDATA windows消息与另一个进程通信。虽然使用ctypes模块发送消息非常简单,但接收答案需要覆盖wx循环,因为wx没有为这种情况提供特定事件 在python2上,我使用ctypes.windl.user32.SetWindowLongPtrW和ctypes.windl.user32.CallWindowProcW函数来获得所需的行为。但是,在python3中,相同的代码会导致OSError:exc

如果这是XY问题,我想做的是:

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

在python2上,我使用
ctypes.windl.user32.SetWindowLongPtrW
ctypes.windl.user32.CallWindowProcW
函数来获得所需的行为。但是,在python3中,相同的代码会导致
OSError:exception:access违例写入

据我所知,python2
ctypes
模块和python3
ctypes
模块之间的唯一区别在于它们处理字符串的方式

我还读到,这两个版本在内存布局上存在差异,但由于我不是C专家,我在代码中找不到问题

我已经用python3.7(64位)、python2.7(64位)和wx4.0.7测试了代码(尽管它也可以用wx2.8和python2)

以下是最小可重复性示例:

导入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\u PTR=ctypes.c\u LONG
_LRESULT=_LONG_PTR
_LPCWSTR=ctypes.wintypes.LPCWSTR
_WNDPROC=ctypes.WINFUNCTYPE(_LPARAM,#返回值
_HWND,#第一个参数,句柄
_UINT,#第二个参数,消息id
_WPARAM,#第三个参数,附加消息信息(取决于消息id)
_LPARAM,#第四个参数,附加消息信息(取决于消息id)
)
_SetWindowLongPtrW=ctypes.windell.user32.SetWindowLongPtrW
_SetWindowLongPtrW.argtypes=(\u-HWND,ctypes.c\u-int,\u-WNDPROC)
_SetWindowLongPtrW.restypes=\u WNDPROC
_CallWindowProc=ctypes.windell.user32.CallWindowProcW
_CallWindowProc.argtypes=(\u WNDPROC、\u HWND、\u UINT、\u WPARAM、\u LPARAM)
_CallWindowProc.restypes=\u LRESULT
定义WndCallback(hwnd、msg、wparam、lparam):
打印(hwnd、msg、wparam、lparam)
返回_CallWindowProc(_old_wndproc,hwnd,msg,_WPARAM(WPARAM),_LPARAM(LPARAM))
_mywndproc=_WNDPROC(_WndCallback)
app=wx.app(重定向=False)
frame=wx.frame(无,title='Simple application')
frame.Show()
_old_wndproc=_wndproc(_SetWindowLongPtrW(frame.GetHandle(),win32con.GWL_wndproc,_mywndproc))
如果_old_wndproc==0:
打印(“错误”)
系统出口(1)
app.MainLoop()
编辑:我知道有一个pywin32模块,它可能对我有所帮助。然而,由于代码在python2上工作,我很好奇这里发生了什么。

这里有一个问题:

\u LONG\u PTR=ctypes.c\u LONG
_LRESULT=_LONG_PTR
类型
LONG_PTR
是“指针大小的整数”,它在32位和64位进程之间变化。由于您使用的是64位Python,指针是64位的,
LONG\u PTR
应该是:

\u LONG\u PTR=ctypes.c\u LONG
如果您希望为32位和64位Python提供更多可移植的代码,
LPARAM
在Windows标头中也定义为
LONG\u PTR
,因此下面的定义将正确定义32位和64位Python的LONG\u PTR,因为
ctypes
已经基于Python的构建正确定义了它:

\u LONG\u PTR=ctypes.wintypes.LPARAM#或(在您的情况下)LPARAM
在那次更改之后,我用wxPython测试了您的脚本,但仍然有一个问题。我怀疑wxPython编译时没有
UNICODE/\u UNICODE
定义,因此SetWindowLongPtr和CallWindowProc API必须使用
A
版本来检索和调用旧的窗口过程。我做了那个更改,下面的代码可以正常工作

Full code tested with 64-bit Python 3.8.8:
```py
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 = _LPARAM
_LRESULT = _LONG_PTR
_LPCWSTR = ctypes.wintypes.LPCWSTR

_WNDPROC = ctypes.WINFUNCTYPE(_LRESULT,  # 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)
)


_SetWindowLongPtr = ctypes.windll.user32.SetWindowLongPtrA
_SetWindowLongPtr.argtypes = (_HWND, ctypes.c_int, _WNDPROC)
_SetWindowLongPtr.restypes = _WNDPROC

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

@_WNDPROC
def _WndCallback(hwnd, msg, wparam, lparam):
    print(hwnd, msg, wparam, lparam)
    return _CallWindowProc(_old_wndproc, hwnd, msg, wparam, lparam)


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

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

app.MainLoop()
另一方面,在中有一个关于SetWindowLongPtr(与CallWindowProc类似)的注释,暗示了此解决方案:

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


非常好,谢谢。