diff --git a/binding.gyp b/binding.gyp index 855bd4b86f0a3c18c7594212c0e42b6e35bc4001..33774e7ae296f0de39dd94156673c9e773638bf4 100644 --- a/binding.gyp +++ b/binding.gyp @@ -3,7 +3,6 @@ { "target_name": "windows_process_tree", "dependencies": [ - "& process_info, DWORD flags); void GetProcessMemoryUsage(ProcessInfo& process_info); +void GetProcessCreationTime(ProcessInfo& process_info); + void GetCpuUsage(Cpu& cpu_info, bool first_run); #endif // SRC_PROCESS_H_ diff --git a/src/process.cc b/src/process.cc index ad63727362..6f1600570b 100644 --- a/src/process.cc +++ b/src/process.cc @@ -21,7 +21,7 @@ uint32_t GetRawProcessList(std::vector& process_info, if (Process32First(snapshot_handle, &process_entry)) { do { if (process_entry.th32ProcessID != 0) { - ProcessInfo pinfo; + ProcessInfo pinfo{}; pinfo.pid = process_entry.th32ProcessID; pinfo.ppid = process_entry.th32ParentProcessID; @@ -33,17 +33,43 @@ uint32_t GetRawProcessList(std::vector& process_info, GetProcessCommandLine(pinfo); } + if (CREATIONTIME & process_data_flags) { + GetProcessCreationTime(pinfo); + } + strcpy(pinfo.name, process_entry.szExeFile); process_info.push_back(std::move(pinfo)); process_count++; } - } while (process_count < 1024 && Process32Next(snapshot_handle, &process_entry)); + } while (Process32Next(snapshot_handle, &process_entry)); } CloseHandle(snapshot_handle); return process_count; } +void GetProcessCreationTime(ProcessInfo& process_info) { + HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, process_info.pid); + if (hProcess == NULL) { + return; + } + + FILETIME creationTime, exitTime, kernelTime, userTime; + if (GetProcessTimes(hProcess, &creationTime, &exitTime, &kernelTime, &userTime)) { + ULARGE_INTEGER timestamp; + timestamp.LowPart = creationTime.dwLowDateTime; + timestamp.HighPart = creationTime.dwHighDateTime; + constexpr ULONGLONG WINDOWS_EPOCH_OFFSET_100NS = 116444736000000000ULL; + constexpr ULONGLONG HUNDRED_NS_PER_MILLISECOND = 10000ULL; + if (timestamp.QuadPart >= WINDOWS_EPOCH_OFFSET_100NS) { + process_info.creationTimeMs = + (timestamp.QuadPart - WINDOWS_EPOCH_OFFSET_100NS) / HUNDRED_NS_PER_MILLISECOND; + } + } + + CloseHandle(hProcess); +} + void GetProcessMemoryUsage(ProcessInfo& process_info) { DWORD pid = process_info.pid; HANDLE hProcess; diff --git a/src/process_worker.cc b/src/process_worker.cc index f59559d31c..1d61c87a56 100644 --- a/src/process_worker.cc +++ b/src/process_worker.cc @@ -43,6 +43,11 @@ void GetProcessesWorker::OnOK() { Napi::String::New(env, pinfo.commandLine)); } + if ((CREATIONTIME & process_data_flags_) && pinfo.creationTimeMs != 0) { + object.Set("creationTimeMs", + Napi::Number::New(env, static_cast(pinfo.creationTimeMs))); + } + result.Set(i, object); } diff --git a/lib/index.ts b/lib/index.ts index 7b53aad05c..e982c8c0be 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, + CreationTime = 4 } type RequestCallback = (processList: IProcessInfo[]) => void; @@ -81,11 +82,12 @@ export function buildProcessTree(rootPid: number, processList: Iterable ({ + const buildNode = ({ info: { pid, name, memory, commandLine, creationTimeMs }, children }: IProcessInfoNode, depth: number): IProcessTreeNode => ({ pid, name, memory, commandLine, + creationTimeMs, children: depth > 0 ? children.map(c => buildNode(c, depth - 1)) : [], }); diff --git a/lib/index.js b/lib/index.js index e586cd6ead..07ad1b914a 100644 --- a/lib/index.js +++ b/lib/index.js @@ -12,6 +12,7 @@ var ProcessDataFlag; ProcessDataFlag[ProcessDataFlag["None"] = 0] = "None"; ProcessDataFlag[ProcessDataFlag["Memory"] = 1] = "Memory"; ProcessDataFlag[ProcessDataFlag["CommandLine"] = 2] = "CommandLine"; + ProcessDataFlag[ProcessDataFlag["CreationTime"] = 4] = "CreationTime"; })(ProcessDataFlag = exports.ProcessDataFlag || (exports.ProcessDataFlag = {})); // requestInProgress is used for any function that uses CreateToolhelp32Snapshot, as multiple calls // to this cannot be done at the same time. @@ -66,11 +67,12 @@ function buildProcessTree(rootPid, processList, maxDepth = MAX_FILTER_DEPTH) { // • 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 }, depth) => ({ + const buildNode = ({ info: { pid, name, memory, commandLine, creationTimeMs }, children }, depth) => ({ pid, name, memory, commandLine, + creationTimeMs, children: depth > 0 ? children.map(c => buildNode(c, depth - 1)) : [], }); return buildNode(root, maxDepth); diff --git a/typings/windows-process-tree.d.ts b/typings/windows-process-tree.d.ts index 70e242b123..fb1b810707 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, + CreationTime = 4 } export interface IProcessInfo { @@ -24,6 +25,9 @@ declare module '@vscode/windows-process-tree' { * The string returned is at most 512 chars, strings exceeding this length are truncated. */ commandLine?: string; + + /** Process creation time in Unix milliseconds. */ + creationTimeMs?: number; } export interface IProcessCpuInfo extends IProcessInfo { @@ -35,6 +39,7 @@ declare module '@vscode/windows-process-tree' { name: string; memory?: number; commandLine?: string; + creationTimeMs?: number; children: IProcessTreeNode[]; }