Ctypes Issue - Impossible to work with 64-bits DLL versus 32-bits DLL - OSError: exception: access violation reading
Ctypes Issue - Impossible to work with 64-bits DLL versus 32-bits DLL - OSError: exception: access violation reading
经过数小时的研究和主题分析,我找不到解释和正确解决我的问题的方法。
如果您找到答案,非常感谢您。
我的问题总结:
我正在使用 python 3.8(64 位版本)和一个 dll(也是 64 位)。而且,由于指针问题(根据我的理解),尽管有 ctypes 功能(argstype、restype..etc),但我无法将其正确包装到我的 python 代码中。
令我困扰的是,同一个 32 位 DLL,python 32 位与我的 python 代码运行良好。
之后请在此处查找:
- C 原型
- 包装 C 函数的 Python 代码
- C函数在python代码中的用法
C 函数原型(来自 API 描述):
WORD mpc_AddToScenarioPcd(字节、双字、双字、双字、双字、双字、双字、双字、指针(字节)、双字、指针(字节)、VOID*)
我的 Python 包装这个的代码(64 位版本和 32 位版本注释):
from ctypes import *
# DLL Loading ---------------------------------------------------------------------------------------
# 64 Bits -------------------------------------------------------------------------------------------
dllpath = '%s/MP300Com64.dll' % str(os.path.dirname(__file__))
MP300Dll = WinDLL(dllpath)
MP300Dll_CDECL = CDLL(dllpath)
# 64 Bits -------------------------------------------------------------------------------------------
# dllpath = '%s/MP300Com.dll' % str(os.path.dirname(__file__))
# MP300Dll = WinDLL(dllpath)
# MP300Dll_CDECL = CDLL(dllpath)
# Keyword Type definition ---------------------------------------------------------------------------
WORD = c_ushort
DWORD = c_ulong
BYTE = c_ubyte
# Function Wrapped ----------------------------------------------------------------------------------
mpc_AddToScenarioPcd = MP300Dll_CDECL.MPC_AddToScenarioPcd
mpc_AddToScenarioPcd.argtypes = [BYTE, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD,
POINTER(BYTE), DWORD, POINTER(BYTE), POINTER(DWORD)]
mpc_AddToScenarioPcd.restype = WORD
# -- Creation of the First Pointer to the array of Bytes --------------------------------------------
array1 = (BYTE* len(5))(*[BYTE(1),BYTE(2),BYTE(3),BYTE(4),BYTE(5)])
array2 = (BYTE* len(5))(*[BYTE(10),BYTE(20),BYTE(30),BYTE(40),BYTE(50)])
# -- Pointer cast of the array1 and 2 ---------------------------------------------------------------
cast(array1, POINTER(BYTE))
cast(array2, POINTER(BYTE))
# VOID* obj creation --------------------------------------------------------------------------------
param = c_void_p()
# -- Function Usage through Python Code -------------------------------------------------------------
mpc_AddToScenarioPcd(BYTE(0),
DWORD(0),
(22),
DWORD(1),
DWORD(1),
DWORD(1),
DWORD(1),
DWORD(2),
array1,
DWORD(10),
array2,
param)
使用 Python 3.4(32 位版本)和 DLL 32 位:
=> 一切正常,没有错误。
使用 Python 3.8(64 位版本)和 DLL 64 位:
=> 我得到这个异常:OSError:异常:访问冲突读取 0x000000005EC0B808
我需要使用 64 位版本,因此我被这个问题阻止了...如果有人可以向我解释我的指针(或类型用法..)在 64 位中有什么问题,还有,如何解决,不胜感激!!
再次非常感谢您为我的主题提供的所有专业知识和工作,
PS:对于非功能性代码和无法重现异常感到抱歉
这里是示例 DLL 代码和最小的 ctypes
代码,适用于 32 位和 64 位 Python。注意以下几点:
ctypes.wintypes
中有预定义的 Windows 类型。
- 当
.argtypes
和 .restype
设置正确时,检查参数数量和类型并根据需要在 Python 和 C 之间转换。通常不需要额外的转换和包装。
CDLL
对应 32 位 __cdecl
调用约定。 WinDLL
对应32位__stdcall
。在 64 位中,只有一种调用约定。它在 Linux 和 Windows 之间的实现方式不同,但仍然只有一个,因此 CDLL
和 WinDLL
都可以工作,但使用正确的 32 位可移植性。
test.c
#include <windows.h>
#include <stdio.h>
__declspec(dllexport)
WORD mpc_AddToScenarioPcd(BYTE b, DWORD d1, DWORD d2, DWORD d3, DWORD d4, DWORD d5, DWORD d6, DWORD d7, LPBYTE pb1, DWORD d8, LPBYTE pb2, LPVOID pv) {
printf("%hhu %u %u %u %u %u %u %u %p %u %p %p\n",b,d1,d2,d3,d4,d5,d6,d7,pb1,d8,pb2,pv);
for(int i = 0; i < 5; ++i)
printf("%hhu %hhu\n",pb1[i],pb2[i]);
return 1234;
}
test.py
from ctypes import *
from ctypes.wintypes import * # pre-defined Windows types
dll = CDLL('./test')
dll.mpc_AddToScenarioPcd.argtypes = BYTE,DWORD,DWORD,DWORD,DWORD,DWORD,DWORD,DWORD,LPBYTE,DWORD,LPBYTE,LPVOID
dll.mpc_AddToScenarioPcd.restype = WORD
b1 = (BYTE * 5)(1,2,3,4,5)
b2 = (BYTE * 5)(10,20,30,40,50)
print(f'{addressof(b1):016X} {addressof(b2):016X}')
result = dll.mpc_AddToScenarioPcd(100,1,2,3,4,5,6,7,b1,8,b2,None)
print(result)
输出
注意 Python 和 C 中打印的地址相同。
00000194D0447A90 00000194D0447C10
100 1 2 3 4 5 6 7 00000194D0447A90 8 00000194D0447C10 0000000000000000
1 10
2 20
3 30
4 40
5 50
1234
经过数小时的研究和主题分析,我找不到解释和正确解决我的问题的方法。 如果您找到答案,非常感谢您。
我的问题总结:
我正在使用 python 3.8(64 位版本)和一个 dll(也是 64 位)。而且,由于指针问题(根据我的理解),尽管有 ctypes 功能(argstype、restype..etc),但我无法将其正确包装到我的 python 代码中。
令我困扰的是,同一个 32 位 DLL,python 32 位与我的 python 代码运行良好。
之后请在此处查找:
- C 原型
- 包装 C 函数的 Python 代码
- C函数在python代码中的用法
C 函数原型(来自 API 描述): WORD mpc_AddToScenarioPcd(字节、双字、双字、双字、双字、双字、双字、双字、指针(字节)、双字、指针(字节)、VOID*)
我的 Python 包装这个的代码(64 位版本和 32 位版本注释):
from ctypes import *
# DLL Loading ---------------------------------------------------------------------------------------
# 64 Bits -------------------------------------------------------------------------------------------
dllpath = '%s/MP300Com64.dll' % str(os.path.dirname(__file__))
MP300Dll = WinDLL(dllpath)
MP300Dll_CDECL = CDLL(dllpath)
# 64 Bits -------------------------------------------------------------------------------------------
# dllpath = '%s/MP300Com.dll' % str(os.path.dirname(__file__))
# MP300Dll = WinDLL(dllpath)
# MP300Dll_CDECL = CDLL(dllpath)
# Keyword Type definition ---------------------------------------------------------------------------
WORD = c_ushort
DWORD = c_ulong
BYTE = c_ubyte
# Function Wrapped ----------------------------------------------------------------------------------
mpc_AddToScenarioPcd = MP300Dll_CDECL.MPC_AddToScenarioPcd
mpc_AddToScenarioPcd.argtypes = [BYTE, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD,
POINTER(BYTE), DWORD, POINTER(BYTE), POINTER(DWORD)]
mpc_AddToScenarioPcd.restype = WORD
# -- Creation of the First Pointer to the array of Bytes --------------------------------------------
array1 = (BYTE* len(5))(*[BYTE(1),BYTE(2),BYTE(3),BYTE(4),BYTE(5)])
array2 = (BYTE* len(5))(*[BYTE(10),BYTE(20),BYTE(30),BYTE(40),BYTE(50)])
# -- Pointer cast of the array1 and 2 ---------------------------------------------------------------
cast(array1, POINTER(BYTE))
cast(array2, POINTER(BYTE))
# VOID* obj creation --------------------------------------------------------------------------------
param = c_void_p()
# -- Function Usage through Python Code -------------------------------------------------------------
mpc_AddToScenarioPcd(BYTE(0),
DWORD(0),
(22),
DWORD(1),
DWORD(1),
DWORD(1),
DWORD(1),
DWORD(2),
array1,
DWORD(10),
array2,
param)
使用 Python 3.4(32 位版本)和 DLL 32 位:
=> 一切正常,没有错误。
使用 Python 3.8(64 位版本)和 DLL 64 位:
=> 我得到这个异常:OSError:异常:访问冲突读取 0x000000005EC0B808
我需要使用 64 位版本,因此我被这个问题阻止了...如果有人可以向我解释我的指针(或类型用法..)在 64 位中有什么问题,还有,如何解决,不胜感激!!
再次非常感谢您为我的主题提供的所有专业知识和工作,
PS:对于非功能性代码和无法重现异常感到抱歉
这里是示例 DLL 代码和最小的 ctypes
代码,适用于 32 位和 64 位 Python。注意以下几点:
ctypes.wintypes
中有预定义的 Windows 类型。- 当
.argtypes
和.restype
设置正确时,检查参数数量和类型并根据需要在 Python 和 C 之间转换。通常不需要额外的转换和包装。 CDLL
对应 32 位__cdecl
调用约定。WinDLL
对应32位__stdcall
。在 64 位中,只有一种调用约定。它在 Linux 和 Windows 之间的实现方式不同,但仍然只有一个,因此CDLL
和WinDLL
都可以工作,但使用正确的 32 位可移植性。
test.c
#include <windows.h>
#include <stdio.h>
__declspec(dllexport)
WORD mpc_AddToScenarioPcd(BYTE b, DWORD d1, DWORD d2, DWORD d3, DWORD d4, DWORD d5, DWORD d6, DWORD d7, LPBYTE pb1, DWORD d8, LPBYTE pb2, LPVOID pv) {
printf("%hhu %u %u %u %u %u %u %u %p %u %p %p\n",b,d1,d2,d3,d4,d5,d6,d7,pb1,d8,pb2,pv);
for(int i = 0; i < 5; ++i)
printf("%hhu %hhu\n",pb1[i],pb2[i]);
return 1234;
}
test.py
from ctypes import *
from ctypes.wintypes import * # pre-defined Windows types
dll = CDLL('./test')
dll.mpc_AddToScenarioPcd.argtypes = BYTE,DWORD,DWORD,DWORD,DWORD,DWORD,DWORD,DWORD,LPBYTE,DWORD,LPBYTE,LPVOID
dll.mpc_AddToScenarioPcd.restype = WORD
b1 = (BYTE * 5)(1,2,3,4,5)
b2 = (BYTE * 5)(10,20,30,40,50)
print(f'{addressof(b1):016X} {addressof(b2):016X}')
result = dll.mpc_AddToScenarioPcd(100,1,2,3,4,5,6,7,b1,8,b2,None)
print(result)
输出
注意 Python 和 C 中打印的地址相同。
00000194D0447A90 00000194D0447C10
100 1 2 3 4 5 6 7 00000194D0447A90 8 00000194D0447C10 0000000000000000
1 10
2 20
3 30
4 40
5 50
1234