这是使用内存映射将值写入内存的代码。当我尝试运行代码时, 在memcpy(pBuf,szMsg,len(szMsg))OSError中 出现错误 “文件“ MMF.py”,第26行,OSError:异常:访问冲突写入0xFFFFFFFFFA1C001B”
import msvcrt, mmap import ctypes from ctypes import * FILE_MAP_ALL_ACCESS = 0x04 INVALID_HANDLE_VALUE = 0xFFFFFFFF SHMEMSIZE = 256 PAGE_READWRITE = 0x04 szName = ctypes.c_wchar_p("MyFileMappingObject") szMsg = "Message from Python(ctypes) process" hMapObject = windll.kernel32.CreateFileMappingA(INVALID_HANDLE_VALUE,None, PAGE_READWRITE, 0, SHMEMSIZE, szName) print("Handle:",hMapObject) if (hMapObject == 0): print("Could not open file mapping object") raise WinError() pBuf = windll.kernel32.MapViewOfFile(hMapObject, FILE_MAP_ALL_ACCESS,0, 0, SHMEMSIZE) print("Buffer Starting Addr:",pBuf) if (pBuf == 0): print("Could not map view of file") raise WinError() else: print(len(szMsg)) memcpy = cdll.msvcrt.memcpy memcpy(pBuf, szMsg, len(szMsg)) shmem = mmap.mmap(0, 256, "MyFileMappingObject_ctypes", mmap.ACCESS_WRITE) shmem.write("Message Python process") msvcrt.getch() windll.kernel32.UnmapViewOfFile(pBuf) windll.kernel32.CloseHandle(hMapObject) shmem.close()
在此之前,这里(几乎)是您需要的一切:[Python 3.Docs]:ctypes- Python的外部函数库。没有解释 ctypes.wintypes ,但是您可以通过dir(ctypes.wintypes)在 Python 控制台中运行来查看其导出。请注意,所有这些类型都是简单的 CTypes 类型,仅出于约定/一致性/可读性的目的而使用它们。
dir(ctypes.wintypes)
您更新的代码( code00.py ):
import sys import ctypes as ct import ctypes.wintypes as wt import mmap import msvcrt def main(*argv): FILE_MAP_ALL_ACCESS = 0x000F001F INVALID_HANDLE_VALUE = -1 SHMEMSIZE = 0x100 PAGE_READWRITE = 0x04 kernel32_dll = ct.windll.kernel32 msvcrt_dll = ct.cdll.msvcrt # To be avoided CreateFileMapping = kernel32_dll.CreateFileMappingW CreateFileMapping.argtypes = (wt.HANDLE, wt.LPVOID, wt.DWORD, wt.DWORD, wt.DWORD, wt.LPCWSTR) CreateFileMapping.restype = wt.HANDLE MapViewOfFile = kernel32_dll.MapViewOfFile MapViewOfFile.argtypes = (wt.HANDLE, wt.DWORD, wt.DWORD, wt.DWORD, ct.c_ulonglong) MapViewOfFile.restype = wt.LPVOID memcpy = msvcrt_dll.memcpy memcpy.argtypes = (ct.c_void_p, ct.c_void_p, ct.c_size_t) memcpy.restype = wt.LPVOID RtlCopyMemory = kernel32_dll.RtlCopyMemory RtlCopyMemory.argtypes = (wt.LPVOID, wt.LPCVOID, ct.c_ulonglong) UnmapViewOfFile = kernel32_dll.UnmapViewOfFile UnmapViewOfFile.argtypes = (wt.LPCVOID,) UnmapViewOfFile.restype = wt.BOOL CloseHandle = kernel32_dll.CloseHandle CloseHandle.argtypes = (wt.HANDLE,) CloseHandle.restype = wt.BOOL GetLastError = kernel32_dll.GetLastError file_mapping_name_ptr = ct.c_wchar_p("MyFileMappingObject") msg = "Message from Python(ctypes) process" msg_ptr = ct.c_wchar_p(msg) mapping_handle = CreateFileMapping(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, 0, SHMEMSIZE, file_mapping_name_ptr) print("Mapping object handle: 0x{:016X}".format(mapping_handle)) if not mapping_handle: print("Could not open file mapping object: {:d}".format(GetLastError())) raise ct.WinError() mapped_view_ptr = MapViewOfFile(mapping_handle, FILE_MAP_ALL_ACCESS, 0, 0, SHMEMSIZE) print("Mapped view addr: 0x{:016X}".format(mapped_view_ptr)) if not mapped_view_ptr: print("Could not map view of file: {:d}".format(GetLastError())) CloseHandle(mapping_handle) raise ct.WinError() byte_len = len(msg) * ct.sizeof(ct.c_wchar) print("Message length: {:d} chars ({:d} bytes)".format(len(msg), byte_len)) memcpy(mapped_view_ptr, msg_ptr, byte_len) # Comment this line RtlCopyMemory(mapped_view_ptr, msg_ptr, byte_len) # Python vriant shmem = mmap.mmap(0, 256, "MyFileMappingObject_ctypes", mmap.ACCESS_WRITE) shmem.write(b"Message Python process") print("Hit a key to clean all memory maps and exit...") msvcrt.getch() UnmapViewOfFile(mapped_view_ptr) CloseHandle(mapping_handle) shmem.close() if __name__ == "__main__": print("Python {0:s} {1:d}bit on {2:s}\n".format(" ".join(item.strip() for item in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform)) main(*sys.argv[1:]) print("\nDone.")
注意事项 :
为函数添加了 argtypes 和 restype 。详细信息可以在“ 指定所需的参数类型(函数原型) ”和“ 返回类型 ”部分中找到,当然也可以在函数声明的MS.Docs中看到。这是 未定义行为 ( UB ),尤其是在 64位上 :[[SO]:通过ctypes从Python调用的C函数返回错误值
的memcpy 的2次参数是一个 Python的 字符串,它是 不 相同的 字符* 地址(更不用说在 Python 3中 ,字符串 为wchar_t 基于),其 的memcpy 预计,这将可能也产生 UB
常数:
FILE_MAP_ALL_ACCESS 的值为 0x000F001F (从 VStudio 2015 打印出该值), 0x04 对应于 FILE_MAP_READ
将 INVALID_HANDLE_VALUE 的旧值转换为 HANDLE时 出错,将其更改为-1(如 handleapi.h所示 )
您正在使用 c_wchar_p 调用 CreateFileMappingA 。,将设置仅由1个的名ST从该映射对象提供的字符串的字符,因为每个 wchar_t的 由2个字节: 0×00 加上相应的 字符 值- “ 甲* ”将被表示为 0×00 0×41 (通常这是 不 正确-尤其是 0x00 部分,但在我们的例子中是)-因此 lpName 参数中的第二个 字符 (由于 little-endianness )将为 0x00 ( NUL ) __ *** ______
根据上面的页面:
通过访问标准C库cdll.msvcrt将使用该库的过时版本,该版本可能与Python所使用的版本不兼容。
cdll.msvcrt
因此,我还添加了[MS.Docs]:RtlCopyMemory函数来替换 memcpy (您可以注释掉其行,我在此处保留它只是为了表明它可以工作),如示例中所示([MS.Docs]:创建命名共享内存),你采取了从代码,并试图将其转换([ minwinbase.h : #36 ] #define CopyMemory RtlCopyMemory)
#define CopyMemory RtlCopyMemory
将命名约定更改为与 Python 兼容([Python]:PEP 8-Python代码样式指南)
其他(非关键)更改(输出格式,为了更好的结构而移动代码行等等)
输出 :
[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q048788549]> “e:\Work\Dev\VEnvs\py_pc064_03.07.06_test0\Scripts\python.exe” code00.py Python 3.7.6 (tags/v3.7.6:43364a7ae0, Dec 19 2019, 00:42:30) [MSC v.1916 64 bit (AMD64)] 64bit on win32 Mapping object handle: 0x000000000000022C Mapped view addr: 0x00000192912B0000 Message length: 35 chars (70 bytes) Hit a key to clean all memory maps and exit... Done.
[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q048788549]>
“e:\Work\Dev\VEnvs\py_pc064_03.07.06_test0\Scripts\python.exe” code00.py Python 3.7.6 (tags/v3.7.6:43364a7ae0, Dec 19 2019, 00:42:30) [MSC v.1916 64 bit (AMD64)] 64bit on win32
Mapping object handle: 0x000000000000022C Mapped view addr: 0x00000192912B0000 Message length: 35 chars (70 bytes) Hit a key to clean all memory maps and exit... Done.