Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions binding.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@
"src/cpu_worker.cc",
"src/process.cc",
"src/process_worker.cc",
"src/process_commandline.cc"
"src/process_commandline.cc",
"src/process_owner.cc"
],
"include_dirs": [],
"libraries": [ 'psapi.lib' ],
"libraries": [ 'psapi.lib', 'advapi32.lib' ],
"msvs_configuration_attributes": {
"SpectreMitigation": "Spectre"
},
Expand Down
23 changes: 20 additions & 3 deletions lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import { IProcessInfo, IProcessTreeNode, IProcessCpuInfo } from '@vscode/windows
export enum ProcessDataFlag {
None = 0,
Memory = 1,
CommandLine = 2
CommandLine = 2,
Owner = 4
}

type RequestCallback = (processList: IProcessInfo[]) => void;
Expand Down Expand Up @@ -81,11 +82,12 @@ export function buildProcessTree(rootPid: number, processList: Iterable<IProcess
// • the properties are inlined/splatted
// • the 'ppid' field is omitted
// • the depth of the tree is limited by `maxDepth`
const buildNode = ({ info: { pid, name, memory, commandLine }, children }: IProcessInfoNode, depth: number): IProcessTreeNode => ({
const buildNode = ({ info: { pid, name, memory, commandLine, owner }, children }: IProcessInfoNode, depth: number): IProcessTreeNode => ({
pid,
name,
memory,
commandLine,
owner,
children: depth > 0 ? children.map(c => buildNode(c, depth - 1)) : [],
});

Expand Down Expand Up @@ -222,8 +224,23 @@ export namespace getProcessTree {
getProcessTree(rootPid, callback, flags);
});
}
export function getSystemProcessList(
callback: (processList: IProcessInfo[]) => void,
flags?: ProcessDataFlag
): void {
if (process.platform !== 'win32') {
throw new Error('getSystemProcessList is only implemented on Windows');
}
getRawProcessList(callback, flags);
}

export namespace getSystemProcessList {
export const __promisify__ = (flags?: ProcessDataFlag): Promise<IProcessInfo[]> =>
new Promise((resolve, reject) => {
getSystemProcessList(list => list ? resolve(list) : reject(new Error('System enumeration failed')), flags);
});
}
// Since symbol properties can't be declared via namespace merging, we just define __promisify__ that way and
// and manually set the "modern" promisify symbol: https://github.com/microsoft/TypeScript/issues/36813
[getProcessTree, getProcessList, getProcessCpuUsage].forEach(func =>
[getProcessTree, getProcessList, getProcessCpuUsage, getSystemProcessList].forEach(func =>
Object.defineProperty(func, promisify.custom, { enumerable: false, value: func.__promisify__ }));
6 changes: 5 additions & 1 deletion src/process.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include "process.h"
#include "process_commandline.h"
#include "process_owner.h"

#include <tlhelp32.h>
#include <psapi.h>
Expand Down Expand Up @@ -32,7 +33,10 @@ uint32_t GetRawProcessList(std::vector<ProcessInfo>& process_info,
if (COMMANDLINE & process_data_flags) {
GetProcessCommandLine(pinfo);
}


if (OWNER & process_data_flags) {
GetProcessOwner(pinfo);
}
strcpy(pinfo.name, process_entry.szExeFile);
process_info.push_back(std::move(pinfo));
process_count++;
Expand Down
4 changes: 3 additions & 1 deletion src/process.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@ struct ProcessInfo {
DWORD ppid;
DWORD memory; // Reported in bytes
std::string commandLine;
std::string owner;
};

enum ProcessDataFlags {
NONE = 0,
MEMORY = 1,
COMMANDLINE = 2
COMMANDLINE = 2,
OWNER = 4
};

uint32_t GetRawProcessList(std::vector<ProcessInfo>& process_info, DWORD flags);
Expand Down
74 changes: 74 additions & 0 deletions src/process_owner.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

#include "process_owner.h"

#include <windows.h>
#include <sddl.h>
#include <vector>

static inline bool WideToMultiByteACP(const wchar_t* w, int wlen, std::string& out) {
out.clear();
if (!w || wlen <= 0) return false;
const int bytes = ::WideCharToMultiByte(CP_ACP, 0, w, wlen, nullptr, 0, nullptr, nullptr);
if (bytes <= 0) return false;
out.resize(bytes);
return ::WideCharToMultiByte(CP_ACP, 0, w, wlen, out.data(), bytes, nullptr, nullptr) == bytes;
}

bool GetProcessOwner(ProcessInfo& process_info) {
process_info.owner.clear();

HANDLE hProcess = ::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, process_info.pid);
if (!hProcess) return false;

HANDLE hToken = nullptr;
if (!::OpenProcessToken(hProcess, TOKEN_QUERY, &hToken)) {
::CloseHandle(hProcess);
return false;
}

DWORD len = 0;
::GetTokenInformation(hToken, TokenUser, nullptr, 0, &len);
if (!len) { ::CloseHandle(hToken); ::CloseHandle(hProcess); return false; }

std::vector<BYTE> buf(len);
if (!::GetTokenInformation(hToken, TokenUser, buf.data(), len, &len)) {
::CloseHandle(hToken); ::CloseHandle(hProcess);
return false;
}
const TOKEN_USER* tu = reinterpret_cast<const TOKEN_USER*>(buf.data());

DWORD nameLen = 0, domLen = 0; SID_NAME_USE use;
::LookupAccountSidW(nullptr, tu->User.Sid, nullptr, &nameLen, nullptr, &domLen, &use);

std::wstring name(nameLen, L'\0');
std::wstring domain(domLen, L'\0');
if (::LookupAccountSidW(nullptr, tu->User.Sid, name.data(), &nameLen, domain.data(), &domLen, &use)) {
name.resize(nameLen);
domain.resize(domLen);
std::wstring domUser;
domUser.reserve(domain.size() + 1 + name.size());
domUser.append(domain).append(L"\\").append(name);

const int wlen = static_cast<int>(domUser.size());
(void)WideToMultiByteACP(domUser.c_str(), wlen, process_info.owner);

::CloseHandle(hToken);
::CloseHandle(hProcess);
return !process_info.owner.empty();
}

LPWSTR sidStr = nullptr;
if (::ConvertSidToStringSidW(tu->User.Sid, &sidStr) && sidStr) {
const int wlen = static_cast<int>(::lstrlenW(sidStr));
(void)WideToMultiByteACP(sidStr, wlen, process_info.owner);
::LocalFree(sidStr);
}

::CloseHandle(hToken);
::CloseHandle(hProcess);
return !process_info.owner.empty();
}
13 changes: 13 additions & 0 deletions src/process_owner.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

#ifndef SRC_PROCESS_OWNER_H_
#define SRC_PROCESS_OWNER_H_

#include "process.h"

bool GetProcessOwner(ProcessInfo& process_info);

#endif
3 changes: 3 additions & 0 deletions src/process_worker.cc
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ void GetProcessesWorker::OnOK() {
object.Set("commandLine",
Napi::String::New(env, pinfo.commandLine));
}
if ((OWNER & process_data_flags_) && !pinfo.owner.empty()) {
object.Set("owner", Napi::String::New(env, pinfo.owner));
}

result.Set(i, object);
}
Expand Down
20 changes: 19 additions & 1 deletion typings/windows-process-tree.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ declare module '@vscode/windows-process-tree' {
export enum ProcessDataFlag {
None = 0,
Memory = 1,
CommandLine = 2
CommandLine = 2,
Owner = 4
}

export interface IProcessInfo {
Expand All @@ -24,6 +25,8 @@ declare module '@vscode/windows-process-tree' {
* The string returned is at most 512 chars, strings exceeding this length are truncated.
*/
commandLine?: string;

owner?: string;
}

export interface IProcessCpuInfo extends IProcessInfo {
Expand All @@ -35,6 +38,7 @@ declare module '@vscode/windows-process-tree' {
name: string;
memory?: number;
commandLine?: string;
owner?: string;
children: IProcessTreeNode[];
}

Expand Down Expand Up @@ -72,4 +76,18 @@ declare module '@vscode/windows-process-tree' {
namespace getProcessCpuUsage {
function __promisify__(processList: IProcessInfo[]): Promise<IProcessCpuInfo[]>;
}

/**
* Returns a flat, system-wide list of processes.
* @param callback - The callback to use with the returned list of processes.
* @param flags - The flags for what process data should be included.
*/
export function getSystemProcessList(
callback: (processList: IProcessInfo[]) => void,
flags?: ProcessDataFlag
): void;

namespace getSystemProcessList {
function __promisify__(flags?: ProcessDataFlag): Promise<IProcessInfo[]>;
}
}
6 changes: 6 additions & 0 deletions typings/windows-process-tree/promises.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,10 @@ declare module '@vscode/windows-process-tree/promises' {
* @param processList - The list of processes.
*/
export function getProcessCpuUsage(processList: IProcessInfo[]): Promise<IProcessCpuInfo[]>;

/**
* Returns a flat, system-wide list of processes.
* @param flags - Flags for what process data should be included (e.g., ProcessDataFlag.Owner).
*/
export function getSystemProcessList(flags?: ProcessDataFlag): Promise<IProcessInfo[]>;
}