Iterate through processes, yielding process ID and properties of each.

    Example usage::

        >>> for pid, info in _iter_process():
        ...     print(pid, '->', info)
        1509 -> {'parent_pid': 1201, 'executable': 'python.exe'}

def _iter_process():
    """Iterate through processes, yielding process ID and properties of each.

    Example usage::

        >>> for pid, info in _iter_process():
        ...     print(pid, '->', info)
        1509 -> {'parent_pid': 1201, 'executable': 'python.exe'}
    """
    # TODO: Process32{First,Next} does not return full executable path, only
    # the name. To get the full path, Module32{First,Next} is needed, but that
    # does not contain parent process information. We probably need to call
    # BOTH to build the correct process tree.
    h_process = windll.kernel32.CreateToolhelp32Snapshot(
        2,  # dwFlags=TH32CS_SNAPPROCESS (include all processes).
        0,  # th32ProcessID=0 (the current process).
    )
    if h_process == INVALID_HANDLE_VALUE:
        raise WinError()
    pe = PROCESSENTRY32()
    pe.dwSize = sizeof(PROCESSENTRY32)
    success = windll.kernel32.Process32First(h_process, byref(pe))
    while True:
        if not success:
            errcode = windll.kernel32.GetLastError()
            if errcode == ERROR_NO_MORE_FILES:
                # No more processes to iterate through, we're done here.
                return
            elif errcode == ERROR_INSUFFICIENT_BUFFER:
                # This is likely because the file path is longer than the
                # Windows limit. Just ignore it, it's likely not what we're
                # looking for. We can fix this when it actually matters. (#8)
                continue
            raise WinError()

        # The executable name would be encoded with the current code page if
        # we're in ANSI mode (usually). Try to decode it into str/unicode,
        # replacing invalid characters to be safe (not thoeratically necessary,
        # I think). Note that we need to use 'mbcs' instead of encoding
        # settings from sys because this is from the Windows API, not Python
        # internals (which those settings reflect). (pypa/pipenv#3382)
        executable = pe.szExeFile
        if isinstance(executable, bytes):
            executable = executable.decode('mbcs', 'replace')

        info = {'executable': executable}
        if pe.th32ParentProcessID:
            info['parent_pid'] = pe.th32ParentProcessID
        yield pe.th32ProcessID, info
        success = windll.kernel32.Process32Next(h_process, byref(pe))