tasklist command with description

前端 未结 1 886
隐瞒了意图╮
隐瞒了意图╮ 2021-01-20 18:40

I am trying to figure out a tasklist command that gives the Description aswell as shown in the Taskmangaer UI?I am trying to run it from python,if

相关标签:
1条回答
  • 2021-01-20 19:17

    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'
    

    Code Update for Python3 (use encode for bytes-wise search):

    s1 = "\r\r\n\r\r\n".encode()
    s2 = "\r\r\n".encode()
    for task in wmi_out.strip().split(s1):
       wmi_entries.append(dict(e.split("=".encode(), 1) for e in task.strip().split(s2)))
    

    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.

    0 讨论(0)
提交回复
热议问题