From 96d4692ad9d8cf1fe75646f9b91d240c07167193 Mon Sep 17 00:00:00 2001 From: "AzureAD\\MohammedChaibi" Date: Mon, 18 Aug 2025 15:29:18 +0200 Subject: [PATCH] Export getRawProcessTree as getSystemProcessList and added flag to get owner --- binding.gyp | 5 +- lib/index.ts | 23 ++++++- src/process.cc | 6 +- src/process.h | 4 +- src/process_owner.cc | 74 ++++++++++++++++++++++ src/process_owner.h | 13 ++++ src/process_worker.cc | 3 + typings/windows-process-tree.d.ts | 20 +++++- typings/windows-process-tree/promises.d.ts | 6 ++ 9 files changed, 146 insertions(+), 8 deletions(-) create mode 100644 src/process_owner.cc create mode 100644 src/process_owner.h diff --git a/binding.gyp b/binding.gyp index c740046..149aab7 100644 --- a/binding.gyp +++ b/binding.gyp @@ -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" }, diff --git a/lib/index.ts b/lib/index.ts index 065c338..4e94b23 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -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; @@ -81,11 +82,12 @@ export function buildProcessTree(rootPid: number, processList: Iterable ({ + 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)) : [], }); @@ -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 => + 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__ })); diff --git a/src/process.cc b/src/process.cc index ad63727..96e26e8 100644 --- a/src/process.cc +++ b/src/process.cc @@ -5,6 +5,7 @@ #include "process.h" #include "process_commandline.h" +#include "process_owner.h" #include #include @@ -32,7 +33,10 @@ uint32_t GetRawProcessList(std::vector& 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++; diff --git a/src/process.h b/src/process.h index 3c9b852..946cd23 100644 --- a/src/process.h +++ b/src/process.h @@ -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& process_info, DWORD flags); diff --git a/src/process_owner.cc b/src/process_owner.cc new file mode 100644 index 0000000..aa50ae3 --- /dev/null +++ b/src/process_owner.cc @@ -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 +#include +#include + +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 buf(len); + if (!::GetTokenInformation(hToken, TokenUser, buf.data(), len, &len)) { + ::CloseHandle(hToken); ::CloseHandle(hProcess); + return false; + } + const TOKEN_USER* tu = reinterpret_cast(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(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(::lstrlenW(sidStr)); + (void)WideToMultiByteACP(sidStr, wlen, process_info.owner); + ::LocalFree(sidStr); + } + + ::CloseHandle(hToken); + ::CloseHandle(hProcess); + return !process_info.owner.empty(); +} diff --git a/src/process_owner.h b/src/process_owner.h new file mode 100644 index 0000000..9a6c770 --- /dev/null +++ b/src/process_owner.h @@ -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 diff --git a/src/process_worker.cc b/src/process_worker.cc index f59559d..d3cee2e 100644 --- a/src/process_worker.cc +++ b/src/process_worker.cc @@ -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); } diff --git a/typings/windows-process-tree.d.ts b/typings/windows-process-tree.d.ts index 996868e..4d668fe 100644 --- a/typings/windows-process-tree.d.ts +++ b/typings/windows-process-tree.d.ts @@ -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 { @@ -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 { @@ -35,6 +38,7 @@ declare module '@vscode/windows-process-tree' { name: string; memory?: number; commandLine?: string; + owner?: string; children: IProcessTreeNode[]; } @@ -72,4 +76,18 @@ declare module '@vscode/windows-process-tree' { namespace getProcessCpuUsage { function __promisify__(processList: IProcessInfo[]): Promise; } + + /** + * 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; + } } diff --git a/typings/windows-process-tree/promises.d.ts b/typings/windows-process-tree/promises.d.ts index 25d8422..4b0e462 100644 --- a/typings/windows-process-tree/promises.d.ts +++ b/typings/windows-process-tree/promises.d.ts @@ -37,4 +37,10 @@ declare module '@vscode/windows-process-tree/promises' { * @param processList - The list of processes. */ export function getProcessCpuUsage(processList: IProcessInfo[]): Promise; + + /** + * 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; }