tasklist command with description

不问归期 提交于 2019-12-02 01:57:29

That's a bit trickier than you might imagine and you really need a good reason to go through all the trouble to justify it. First of all, Task Manager UI doesn't get its information from tasklist.exe, although you can get pretty close with:

import csv
import subprocess

try:
    tl_out = subprocess.check_output(["tasklist", "/fo", "csv", "/v"])
except subprocess.CalledProcessError as e:
    print("Call to `tasklist` failed: {}".format(e))
    exit(1)

tl_csv = csv.DictReader(tl_out.splitlines())
for row in tl_csv:
    print(row)  # prints a dict for each task with all available fields
    # Available fields (may vary from platform to platform) are:
    # 'Status', 'CPU Time', 'Image Name', 'Session Name', 'Window Title',
    # 'PID', 'User Name', 'Session#', 'Mem Usage'

However, to get to the Description field (and a lot others from the Task Manager UI) you'll have to pull the data from WMI at the very least. To make matters worse, WMIC on Windows 7 has a bug when exporting to CSV making the whole thing even more complicated as for maximum portability we need to use the list format and parse it ourselves:

import subprocess

try:
    wmi_out = subprocess.check_output(["wmic", "process", "list", "full", "/format:list"])
except subprocess.CalledProcessError as e:
    print("Call to `wmic` failed: {}".format(e))
    exit(1)

# parse the WMI list:
wmi_entries = []
for task in wmi_out.strip().split("\r\r\n\r\r\n"):
    wmi_entries.append(dict(e.split("=", 1) for e in task.strip().split("\r\r\n")))

for row in wmi_entries:
    print(row)  # prints a dict for each task with all available fields
    # Available fields (may vary from platform to platform) are:
    # 'CSName', 'CommandLine', 'Description', 'ExecutablePath', 'ExecutionState', 'Handle',
    # 'HandleCount', 'InstallDate', 'KernelModeTime', 'MaximumWorkingSetSize',
    # 'MinimumWorkingSetSize', 'Name', 'OSName', 'OtherOperationCount', 'OtherTransferCount',
    # 'PageFaults', 'PageFileUsage', 'ParentProcessId', 'PeakPageFileUsage',
    # 'PeakVirtualSize', 'PeakWorkingSetSize', 'Priority', 'PrivatePageCount', 'ProcessId',
    # 'QuotaNonPagedPoolUsage', 'QuotaPagedPoolUsage', 'QuotaPeakNonPagedPoolUsage',
    # 'QuotaPeakPagedPoolUsage', 'ReadOperationCount', 'ReadTransferCount', 'SessionId',
    # 'Status', 'TerminationDate', 'ThreadCount', 'UserModeTime', 'VirtualSize',
    # 'WindowsVersion', 'WorkingSetSize', 'WriteOperationCount', 'WriteTransferCount'

If you don't need all these fields, you can always restrict wmic to get you the fields you want (i.e. wmi_out = subprocess.check_output(["wmic", "process", "get", "ProcessId,ExecutablePath,Description", "/format:list"]) to get only Description per ProcessId).

But don't think your troubles are over - we just started. While we now have the Description field (and a few others to boot), you'll notice that for processes that do not announce their description (most of them, Windows programmers be lazy apparently) or services without a description - the description value just contains the executable name i.e. if you're running plain old Notepad, while Task Manager UI will show you Notepad as Description, its dictionary entry will have notepad.exe - that is because Task Manager UI uses a completely different approach to task list and gets the description directly from the process executable.

So you actually need an additional step to retrieve the executable description directly from its resources table, which is probably the 'easiest' to do by invoking the Win32 API to get to the description, so you need to install the pyWin32 module first:

import subprocess
import win32api

# gets executable description via W32API
def get_executable_desc(path, default=''):
    try:
        language, codepage = win32api.GetFileVersionInfo(path, "\\VarFileInfo\\Translation")[0]
        return win32api.GetFileVersionInfo(path, "\\StringFileInfo\\{:04x}{:04x}\\FileDescription".format(language, codepage)) or default
    except:
        return default

try:
    wmi_out = subprocess.check_output(["wmic", "process", "list", "full", "/format:list"])
except subprocess.CalledProcessError as e:
    print("Call to `tasklist` failed: {}".format(e))
    exit(1)

# parse the WMI list:
wmi_entries = []
for task in wmi_out.strip().split("\r\r\n\r\r\n"):
    entry = dict(e.split("=", 1) for e in task.strip().split("\r\r\n"))
    entry['Description'] = get_executable_desc(entry.get("ExecutablePath", None), entry.get("Description", None))
    wmi_entries.append(entry)

for row in wmi_entries:
    print(row)  # prints a dict for each task with all available fields

Voilà! Descriptions are now populated (where available, or at least hold the executable name), but since we had to use Win32 API to get to the descriptions, we might as well get the tasks list through it - it's faster and more concise:

from win32api import GetFileVersionInfo, OpenProcess
from win32con import PROCESS_QUERY_INFORMATION, PROCESS_VM_READ
from win32process import EnumProcesses, EnumProcessModules, GetModuleFileNameEx
import pywintypes

# gets executable description via W32API
def get_executable_desc(path, default=''):
    try:
        language, codepage = GetFileVersionInfo(path, "\\VarFileInfo\\Translation")[0]
        return GetFileVersionInfo(path, "\\StringFileInfo\\{:04x}{:04x}\\FileDescription".format(language, codepage)) or default
    except:
        return default

# gets the process list via W32API        
def get_process_list():
    proc_list = []
    processes = EnumProcesses()
    if not processes:
        return []  # optionally raise an exception, no ProcessIds could be obtained
    for proc in processes:
        try:
            handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, pywintypes.FALSE, proc)
            modules = EnumProcessModules(handle)
            if not modules:
                continue  # task died in the meantime?
            path = GetModuleFileNameEx(handle, modules[0])
            proc_list.append({"ProcessId": proc, "ExecutablePath": path, "Description": get_executable_desc(path, path)})
        except pywintypes.error as e:
            continue  # optionally report the error stored in `e`
    return proc_list

tasks = get_process_list()
for row in tasks:
    print(row)  # prints a dict for each task with ProcessId, ExecutablePath and Description fields

This will only get ProcessId, ExecutablePath and Description but you can further explore Win32 API if you need more fields.

Again, I don't see of what value the Description field is to go through all this trouble but if you really, really want it - this is how to get it.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!