Python mmap:can';t在不知道其大小的情况下附加到现有区域(窗口)
我试图连接到另一个应用程序创建的现有共享内存区域,而不是用Python编写的(这就是它的插件模块相互通信的方式)。在Windows上,它使用命名的内核对象,而不是文件系统中的文件;Python的mmap模块通过Python mmap:can';t在不知道其大小的情况下附加到现有区域(窗口),python,windows,mmap,Python,Windows,Mmap,我试图连接到另一个应用程序创建的现有共享内存区域,而不是用Python编写的(这就是它的插件模块相互通信的方式)。在Windows上,它使用命名的内核对象,而不是文件系统中的文件;Python的mmap模块通过标记名参数支持这一点。问题是我无法预先知道共享区域的大小-这是另一个应用程序的配置参数,根据预期的数据量进行调整。对于基于文件的共享区域,为大小传递零将使用文件的现有大小,但这显然不适用于标记区域。以下是我尝试的简化版本: import mmap, random TAGNAME = 'S
标记名
参数支持这一点。问题是我无法预先知道共享区域的大小-这是另一个应用程序的配置参数,根据预期的数据量进行调整。对于基于文件的共享区域,为大小传递零将使用文件的现有大小,但这显然不适用于标记区域。以下是我尝试的简化版本:
import mmap, random
TAGNAME = 'SHM_1001'
# This is a simulation of what the other application does.
# The size isn't actually random, I simply don't know in advance what it is.
m1 = mmap.mmap(-1, random.randint(1e3, 1e6), TAGNAME)
# This is what I'm trying to do in my application, to attach to the same region.
m2 = mmap.mmap(-1, 0, TAGNAME)
# WindowsError: [Error 87] The parameter is incorrect
如果我指定一个小的非零大小,那么我可以成功地附加到该区域-但是当然,我只能访问该区域开始处的那么多字节。如果我指定的大小大于区域的实际大小(可能等于它所能拥有的最大大小),我会得到一个访问错误。这个问题在Python2.7和3.4中都存在
将大小传递为零的方法肯定在系统调用级别起作用——这正是此应用程序的每个现有C/C++插件的工作方式——因此问题显然出在Python的mmap()调用包装器中。有什么办法可以让它工作吗?它应该是这样工作的: 如果长度大于文件的当前大小,则文件为 扩展为包含长度字节。如果长度为0,则为最大长度 映射的大小是文件的当前大小,但如果文件为 空窗口引发异常(无法创建空映射) 在Windows上)
但目前看来,这是一个已知的错误:中的参数验证在调用系统服务之前出错,如果调用该服务,将找到现有的部分。当
hFile
为无效时使用0 size\u HANDLE\u VALUE
(-1)无效,因为CreateFileMapping
假定(在本例中错误地)需要从分页文件分配节。我假设C插件正在调用(即)
您可以使用ctypes、PyWin32或C扩展模块。调用OpenFileMappingW
后,调用MapViewOfFile
,然后调用VirtualQuery
以获取映射的区域大小,四舍五入到页面边界
下面是一个使用ctypes的示例
from ctypes import *
from ctypes.wintypes import *
kernel32 = WinDLL('kernel32', use_last_error=True)
FILE_MAP_COPY = 0x0001
FILE_MAP_WRITE = 0x0002
FILE_MAP_READ = 0x0004
FILE_MAP_ALL_ACCESS = 0x001f
FILE_MAP_EXECUTE = 0x0020
PVOID = LPVOID
SIZE_T = c_size_t
class MEMORY_BASIC_INFORMATION(Structure):
_fields_ = (('BaseAddress', PVOID),
('AllocationBase', PVOID),
('AllocationProtect', DWORD),
('RegionSize', SIZE_T),
('State', DWORD),
('Protect', DWORD),
('Type', DWORD))
PMEMORY_BASIC_INFORMATION = POINTER(MEMORY_BASIC_INFORMATION)
def errcheck_bool(result, func, args):
if not result:
raise WinError(get_last_error())
return args
kernel32.VirtualQuery.errcheck = errcheck_bool
kernel32.VirtualQuery.restype = SIZE_T
kernel32.VirtualQuery.argtypes = (
LPCVOID, # _In_opt_ lpAddress
PMEMORY_BASIC_INFORMATION, # _Out_ lpBuffer
SIZE_T) # _In_ dwLength
kernel32.OpenFileMappingW.errcheck = errcheck_bool
kernel32.OpenFileMappingW.restype = HANDLE
kernel32.OpenFileMappingW.argtypes = (
DWORD, # _In_ dwDesiredAccess
BOOL, # _In_ bInheritHandle
LPCWSTR) # _In_ lpName
kernel32.MapViewOfFile.errcheck = errcheck_bool
kernel32.MapViewOfFile.restype = LPVOID
kernel32.MapViewOfFile.argtypes = (
HANDLE, # _In_ hFileMappingObject
DWORD, # _In_ dwDesiredAccess
DWORD, # _In_ dwFileOffsetHigh
DWORD, # _In_ dwFileOffsetLow
SIZE_T) # _In_ dwNumberOfBytesToMap
kernel32.CloseHandle.errcheck = errcheck_bool
kernel32.CloseHandle.argtypes = (HANDLE,)
if __name__ == '__main__':
import mmap
NPAGES = 9
PAGE_SIZE = 4096
TAGNAME = 'SHM_1001'
mm1 = mmap.mmap(-1, PAGE_SIZE * NPAGES, TAGNAME)
hMap = kernel32.OpenFileMappingW(FILE_MAP_ALL_ACCESS, False, TAGNAME)
pBuf = kernel32.MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0)
kernel32.CloseHandle(hMap)
mbi = MEMORY_BASIC_INFORMATION()
kernel32.VirtualQuery(pBuf, byref(mbi), PAGE_SIZE)
assert divmod(mbi.RegionSize, PAGE_SIZE) == (NPAGES, 0)
mm2 = (c_char * mbi.RegionSize).from_address(pBuf)
# write using the mmap object
mm1.seek(100)
mm1.write(b'Windows')
# read using the ctypes array
assert mm2[100:107] == b'Windows'