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

使用ctypes枚举Windows进程时遭遇报错问题求助

解决Windows进程遍历与名称获取的报错问题

错误原因分析

  • 进程ID 0:系统空闲进程,无实际可打开的进程对象,直接跳过即可。
  • 错误码5(拒绝访问):PROCESS_QUERY_INFORMATION权限要求过高,部分系统进程无法获取该权限,需改用更宽松的PROCESS_QUERY_LIMITED_INFORMATION权限(Windows Vista及以上系统支持)。
  • 错误码299(部分完成):部分系统进程(如PID4)受保护,无法通过EnumProcessModules枚举模块,应改用无需枚举模块的API直接获取进程镜像名称。

修正后的代码

import ctypes
import ctypes.wintypes
from pathlib import Path

# Library initializations
psapi = ctypes.windll.psapi
kernel32 = ctypes.windll.kernel32

FORMAT_MESSAGE_FROM_SYSTEM = 0x1000
# 改用更易获取的权限
PROCESS_QUERY_LIMITED_INFORMATION = 0x10000000
MAX_PROCESS_COUNT = 4096

FLAGGED_PROCESSES = ["test.exe"] 


def get_last_error():
    """获取最后错误码及描述"""
    error_code = ctypes.GetLastError()
    buffer = ctypes.create_unicode_buffer(256)
    kernel32.FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, None, error_code, 0, buffer, len(buffer), None)
    return error_code, buffer.value.strip()

def _get_process_name(process_id):
    # 跳过系统空闲进程
    if process_id == 0:
        return None
    
    h_process = kernel32.OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, False, process_id)
    if not h_process:
        # 可选择性打印错误,避免过多日志
        # print(f"打开进程 {process_id} 失败。错误: {get_last_error()}")
        return None
    
    try:
        # 使用QueryFullProcessImageNameW直接获取进程镜像路径
        exe_path = ctypes.create_unicode_buffer(ctypes.wintypes.MAX_PATH)
        size = ctypes.wintypes.DWORD(ctypes.sizeof(exe_path) // 2)
        
        if kernel32.QueryFullProcessImageNameW(h_process, 0, exe_path, ctypes.byref(size)):
            # 从路径中提取文件名
            return Path(exe_path.value).name
        else:
            # print(f"获取进程 {process_id} 路径失败。错误: {get_last_error()}")
            return None
        
    finally:
        kernel32.CloseHandle(h_process)

def is_flagged_process_running():
    DWORD = ctypes.wintypes.DWORD
    DWORD_ARRAY = DWORD * MAX_PROCESS_COUNT
    process_ids = DWORD_ARRAY()
    bytes_returned = DWORD()

    if not psapi.EnumProcesses(ctypes.byref(process_ids), ctypes.sizeof(process_ids), ctypes.byref(bytes_returned)):
        print(f"枚举进程失败。错误: {get_last_error()}")
        return False

    number_of_processes = bytes_returned.value // ctypes.sizeof(DWORD)

    for index in range(number_of_processes):
        process_name = _get_process_name(process_ids[index])
        if process_name and process_name.lower() in FLAGGED_PROCESSES:
            return True

    return False


print(is_flagged_process_running())

关键修改点

  1. 跳过PID 0:直接判断进程ID为0时返回None,避免无效的打开操作。
  2. 替换权限常量:用PROCESS_QUERY_LIMITED_INFORMATION替代PROCESS_QUERY_INFORMATION,降低权限要求,减少拒绝访问错误。
  3. 更换API:使用QueryFullProcessImageNameW直接从进程句柄获取完整路径,无需枚举模块,避免299错误。
  4. 提取文件名:用pathlib.Path从完整路径中提取进程文件名,符合需求。
  5. 可选日志关闭:注释掉部分错误打印,避免过多无关日志干扰,如需调试可取消注释。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 00:27:05