You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ctypes create_string_buffer读取到None后无法继续读取的问题求助

问题分析与彻底修复方案

问题现象

当某次内存读取返回None后,后续所有读取操作都会持续返回空结果,必须关闭并重新启动进程连接才能恢复正常,问题根源出在readString函数的实现上。

原问题代码

def readString(lp_base_address, length):
    try:
        read_buffer = ctypes.create_string_buffer(length)
        lp_number_of_bytes_read = ctypes.c_ulong(0)
        ctypes.windll.kernel32.ReadProcessMemory(self.handle, lp_base_address, read_buffer, length, lp_number_of_bytes_read)
        bufferArray = bytearray(read_buffer)
        found_terminator = bufferArray.find(b'\x00')
        if found_terminator != -1:
            return bufferArray[:found_terminator].decode('utf-8')
        print("[ReadMemory/Error]: terminator not found.\naddress: %s" % hex(lp_base_address))
        return "ERROR"
    except (BufferError, ValueError, TypeError) as error:
        if self.handle:
            self.close()
        self.error_code = self.get_last_error()
        error = {'msg': str(error), 'Handle': self.handle, 'PID': self.pid,
                 'Name': self.name, 'ErrorCode': self.error_code}
        ReadWriteMemoryError(error)

for pointer in listOfManyPointers():
    result = readString(pointer, 50)

当前临时修复代码

for pointer in listOfManyPointers():
    result = readString(pointer, 50)
    if result == None: # TODO fix and remove this block
        process.close()
        connectToProcess()

问题根源

  1. 未校验ReadProcessMemory调用结果:该API读取失败时(比如目标地址无效、进程权限不足),不会触发现有捕获的异常,但会导致read_buffer为无效数据,后续逻辑可能隐式返回None,且无效的进程句柄会被保留,导致后续所有读取都失败。
  2. 参数传递错误:lp_number_of_bytes_read需要通过ctypes.byref传递指针,原代码直接传值无法正确获取实际读取的字节数。
  3. 异常捕获范围不全:Windows API调用可能抛出ctypes.WinError类型的异常,原代码未捕获,会导致程序意外终止或句柄未被清理。
  4. 返回值不明确:当ReadProcessMemory失败且未触发异常时,函数没有明确返回值,会隐式返回None,这就是问题中出现None的直接原因。

彻底修复后的代码

def readString(lp_base_address, length):
    try:
        read_buffer = ctypes.create_string_buffer(length)
        lp_number_of_bytes_read = ctypes.c_ulong(0)
        # 校验API调用是否成功,同时修正参数传递方式
        success = ctypes.windll.kernel32.ReadProcessMemory(
            self.handle,
            lp_base_address,
            read_buffer,
            length,
            ctypes.byref(lp_number_of_bytes_read)
        )
        if not success:
            self.error_code = ctypes.windll.kernel32.GetLastError()
            print(f"[ReadMemory/Error] 读取内存失败,地址: {hex(lp_base_address)},错误码: {self.error_code}")
            # 读取失败立即关闭无效句柄
            if self.handle:
                self.close()
            return None
        
        bufferArray = bytearray(read_buffer)
        found_terminator = bufferArray.find(b'\x00')
        if found_terminator != -1:
            return bufferArray[:found_terminator].decode('utf-8')
        print(f"[ReadMemory/Error]: 未找到终止符,地址: {hex(lp_base_address)}")
        return "ERROR"
    except (BufferError, ValueError, TypeError, ctypes.WinError) as error:
        if self.handle:
            self.close()
        self.error_code = ctypes.windll.kernel32.GetLastError()
        error_info = {
            'msg': str(error),
            'Handle': self.handle,
            'PID': self.pid,
            'Name': self.name,
            'ErrorCode': self.error_code
        }
        ReadWriteMemoryError(error_info)
        return None  # 明确返回值,避免隐式返回None

修复说明

  • 每次调用ReadProcessMemory后立即校验结果,失败时直接关闭无效句柄并返回明确的None,避免后续使用失效句柄。
  • 修正lp_number_of_bytes_read的传递方式,确保能正确获取实际读取的字节数。
  • 扩展异常捕获范围,覆盖Windows API可能抛出的异常类型。
  • 确保函数所有分支都有明确返回值,消除隐式返回None的情况。

修复后即可移除循环中的临时重启逻辑,读取失败时会自动清理无效句柄,重新连接进程后就能正常执行后续读取操作。

内容的提问来源于stack exchange,提问作者emilector

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 03:13:25