Skip to content

Commit

Permalink
fix #599 (Windows): process name() can now be determined for all PIDs
Browse files Browse the repository at this point in the history
  • Loading branch information
giampaolo committed Feb 28, 2015
1 parent 9c8fba0 commit 29defce
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 1 deletion.
2 changes: 2 additions & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ Bug tracker at https://github.com/giampaolo/psutil/issues
- #589: Process.cpu_affinity() accepts any kind of iterable (set, tuple, ...),
not only lists.
- #594: all deprecated APIs were removed.
- #599: [Windows] process name() can now be determined for all processes even
when running as a limited user.

**Bug fixes**

Expand Down
42 changes: 42 additions & 0 deletions psutil/_psutil_windows.c
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,46 @@ psutil_proc_exe(PyObject *self, PyObject *args) {
}


/*
* Return process base name.
* Note: psutil_proc_exe() is attempted first because it's faster
* but it raise AccessDenied for processes owned by other users
* in which case we fall back on using this.
*/
static PyObject *
psutil_proc_name(PyObject *self, PyObject *args) {
long pid;
int ok;
PROCESSENTRY32 pentry;
HANDLE hSnapShot;

if (! PyArg_ParseTuple(args, "l", &pid))
return NULL;
hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, pid);
if (hSnapShot == INVALID_HANDLE_VALUE) {
PyErr_SetFromWindowsErr(0);
return NULL;
}
pentry.dwSize = sizeof(PROCESSENTRY32);
ok = Process32First(hSnapShot, &pentry);
if (! ok) {
PyErr_SetFromWindowsErr(0);
return NULL;
}
while (ok) {
if (pentry.th32ProcessID == pid) {
CloseHandle(hSnapShot);
return Py_BuildValue("s", pentry.szExeFile);
}
ok = Process32Next(hSnapShot, &pentry);
}

CloseHandle(hSnapShot);
NoSuchProcess();
return NULL;
}


/*
* Return process memory information as a Python tuple.
*/
Expand Down Expand Up @@ -3198,6 +3238,8 @@ PsutilMethods[] =
"Return process cmdline as a list of cmdline arguments"},
{"proc_exe", psutil_proc_exe, METH_VARARGS,
"Return path of the process executable"},
{"proc_name", psutil_proc_name, METH_VARARGS,
"Return process name"},
{"proc_kill", psutil_proc_kill, METH_VARARGS,
"Kill the process identified by the given PID"},
{"proc_cpu_times", psutil_proc_cpu_times, METH_VARARGS,
Expand Down
1 change: 1 addition & 0 deletions psutil/_psutil_windows.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ static PyObject* psutil_proc_kill(PyObject* self, PyObject* args);
static PyObject* psutil_proc_memory_info(PyObject* self, PyObject* args);
static PyObject* psutil_proc_memory_info_2(PyObject* self, PyObject* args);
static PyObject* psutil_proc_memory_maps(PyObject* self, PyObject* args);
static PyObject* psutil_proc_name(PyObject* self, PyObject* args);
static PyObject* psutil_proc_num_handles(PyObject* self, PyObject* args);
static PyObject* psutil_proc_open_files(PyObject* self, PyObject* args);
static PyObject* psutil_proc_priority_get(PyObject* self, PyObject* args);
Expand Down
9 changes: 8 additions & 1 deletion psutil/_pswindows.py
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,14 @@ def name(self):
elif self.pid == 4:
return "System"
else:
return os.path.basename(self.exe())
try:
# Note: this will fail with AD for most PIDs owned
# by another user but it's faster.
return os.path.basename(self.exe())
except OSError as err:
if err.errno in ACCESS_DENIED_SET:
return cext.proc_name(self.pid)
raise

@wrap_exceptions
def exe(self):
Expand Down
10 changes: 10 additions & 0 deletions test/_windows.py
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,16 @@ def compare_with_tolerance(ret1, ret2, tolerance):
if failures:
self.fail('\n\n'.join(failures))

def test_compare_name_exe(self):
for p in psutil.process_iter():
try:
a = os.path.basename(p.exe())
b = p.name()
except (psutil.NoSuchProcess, psutil.AccessDenied):
pass
else:
self.assertEqual(a, b)

def test_zombies(self):
# test that NPS is raised by the 2nd implementation in case a
# process no longer exists
Expand Down

0 comments on commit 29defce

Please sign in to comment.