PYTHON - Ctypes:OSError:异常:访问冲突写入0xFFFFFFFFFA1C001B

Posted

技术标签:

【中文标题】PYTHON - Ctypes:OSError:异常:访问冲突写入0xFFFFFFFFFA1C001B【英文标题】:PYTHON - Ctypes : OSError: exception: access violation writing 0xFFFFFFFFFA1C001B 【发布时间】:2018-07-25 02:33:28 【问题描述】:

这是使用内存映射将值写入内存的代码。当我尝试运行代码时,出现错误 “文件“MMF.py”,第 26 行,在 memcpy(pBuf, szMsg, len(szMsg)) 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()

【问题讨论】:

您需要在这些导入上指定 argtypes 和 restype,否则它们将默认为 32 位整数。不是你想要的。 + 你不会将原生函数与 Python 对象混合使用,反之亦然。并且一些常量被错误地使用/定义。 我尝试初始化 argtypes 结果还是一样 @prabakars 那么也许你在“声明”中犯了一个错误。直到我们看到你做了什么,我们才能知道。 【参考方案1】:

首先,这里(几乎)是您需要的一切:[Python 3.Docs]: ctypes - A foreign function library for Python。 ctypes.wintypes 没有解释,但您可以通过在 Python 控制台中运行 dir(ctypes.wintypes) 来查看其导出。请注意,所有这些类型都是简单的 CTypes 类型,我使用它们只是为了约定/一致性/可读性。

您的更新代码(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:dbit 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.")

注意事项

为函数添加了 argtypesrestype。详细信息可以在“指定所需的参数类型(函数原型)”和“返回类型”部分中看到,当然还有MS.Docs 用于函数声明。这是未定义的行为 (UB),尤其是在 64 位 上:[SO]: C function called from Python via ctypes returns incorrect value (@CristiFati's answer)

memcpy 的 2nd 参数是一个 Python 字符串,即 char * 地址不同(更不用说在 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。这将设置一个名称,该名称仅包含为映射对象提供的字符串中的 1st 字符,因为每个 wchar_t 包含 2 个字节:0x00加上相应的 char 值 - 'A' 将表示为 0x00 0x41 (通常这是 not true -特别是 0x00 部分,但在我们的例子中,它是) - 所以 lpName 中的 2ndchar参数(由于 little-endianness)将是 0x00 (NUL)

根据上面的页面:

通过cdll.msvcrt 访问标准 C 库将使用该库的过时版本,该版本可能与 Python 使用的库不兼容。

所以,我还添加了[MS.Docs]: RtlCopyMemory function 来替换memcpy(你可以注释掉它的行,我把它留在那里只是为了表明它可以工作),如示例([MS.Docs]: Creating Named Shared Memory)您从中获取代码并尝试对其进行转换([minwinbase.h: #36]: #define CopyMemory RtlCopyMemory

将命名约定更改为与 Python 兼容 ([Python]: PEP 8 -- Style Guide for Python Code)

其他(非关键)更改(输出格式、移动代码行以获得更好的结构等等...)

输出

[cfati@CFATI-5510-0:e:\Work\Dev\***\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.

【讨论】:

本题基于原C实现:docs.microsoft.com/en-us/windows/win32/memory/…注意,最后的getch_func()应该是关闭内存映射文件之前,否则读者部分赢了不行!

以上是关于PYTHON - Ctypes:OSError:异常:访问冲突写入0xFFFFFFFFFA1C001B的主要内容,如果未能解决你的问题,请参考以下文章

ctypes,python3.5,OSError:异常:访问冲突写入 0x00000000

PYTHON - Ctypes:OSError:异常:访问冲突写入0xFFFFFFFFFA1C001B

ctypes:OSError:异常:访问冲突读取0x00000001

从 Python 访问 errno?

Python ctypes:如何将 ctypes 数组传递给 DLL?

OSError:HDFS 连接失败(Python)