21#pragma comment(lib, "psapi.lib")
40 if (::ProcessIdToSessionId(pe32->th32ProcessID, &dwSess)) proc.
iSessionId =
static_cast<int>(dwSess);
41 if (pe32->th32ProcessID == 0)
return;
42 HANDLE hProc = ::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pe32->th32ProcessID);
44 WCHAR szPath[MAX_PATH] = { 0 }; DWORD dwLen = MAX_PATH;
45 if (::QueryFullProcessImageNameW(hProc, 0, szPath, &dwLen)) proc.
sPath =
String(szPath);
46 PROCESS_MEMORY_COUNTERS pmc;
47 if (::GetProcessMemoryInfo(hProc, &pmc,
sizeof(pmc))) {
56 HANDLE hSnapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
57 if (hSnapshot == INVALID_HANDLE_VALUE)
throw SystemException(
"Failed to create system process snapshot.");
58 PROCESSENTRY32W pe32; pe32.dwSize =
sizeof(PROCESSENTRY32W);
59 if (::Process32FirstW(hSnapshot, &pe32)) {
61 if (pe32.th32ProcessID == 0)
continue;
65 vecProcs.push_back(proc);
67 }
while (::Process32NextW(hSnapshot, &pe32));
69 ::CloseHandle(hSnapshot);
73 static void FastPopulateLinuxProc(
const std::string& dname, ProcessInfo& proc) {
74 proc.iProcessId = std::stoi(dname);
75 std::ifstream commFile(
"/proc/" + dname +
"/comm");
77 if (commFile.is_open() && std::getline(commFile, procComm)) proc.sName =
String(procComm.c_str());
78 std::ifstream statmFile(
"/proc/" + dname +
"/statm");
79 unsigned long size = 0, resident = 0;
80 if (statmFile >> size >> resident) {
81 long pageSize = sysconf(_SC_PAGE_SIZE);
82 proc.memory.lPhysicalMemoryBytes =
static_cast<long long>(resident * pageSize);
88 DIR* dir = ::opendir(
"/proc");
90 struct dirent* entry =
nullptr;
91 while ((entry = ::readdir(dir)) !=
nullptr) {
92 if (entry->d_type == DT_DIR) {
93 std::string dname = entry->d_name;
94 if (std::all_of(dname.begin(), dname.end(), ::isdigit)) {
96 FastPopulateLinuxProc(dname, proc);
97 if (iSessionId == -1 || proc.iSessionId == iSessionId) vecProcs.push_back(proc);
110 class ProcessStreamer::Impl :
public Object {
112 ProcessStreamOptions m_options;
120 std::atomic<bool> m_bRunning;
121 std::atomic<bool> m_bCancelled;
124 explicit Impl(
const ProcessStreamOptions& options)
125 : m_options(options), m_bRunning(false), m_bCancelled(false) {}
131 void DispatchProcess(
const ProcessInfo& proc) {
132 ProcessEventArgs args(proc);
136 void DispatchBatch(
const Collections::Generic::List<ProcessInfo>& lstBatch) {
137 ProcessBatchEventArgs args(lstBatch);
138 BatchReady.Invoke(
this, args);
141 void DispatchUpdated(
const ProcessInfo& proc) {
142 ProcessEventArgs args(proc);
143 ProcessUpdated.Invoke(
this, args);
146 void DispatchCompleted() {
150 void DispatchError(
const Exception& ex) {
151 ProcessStreamErrorEventArgs args(ex.What());
152 Error.Invoke(
this, args);
155 void RunTier1(std::vector<ProcessInfo>& vecProcs) {
156 Collections::Generic::List<ProcessInfo> batch;
157 int iBatchLimit = (m_options.iBatchSize > 0) ? m_options.iBatchSize : 25;
158 for (
size_t i = 0; i < vecProcs.size(); ++i) {
159 if (m_bCancelled.load())
break;
160 DispatchProcess(vecProcs[i]);
161 batch.Add(vecProcs[i]);
162 if (batch.GetCount() >= iBatchLimit) {
163 DispatchBatch(batch);
168 if (batch.GetCount() > 0 && !m_bCancelled.load()) DispatchBatch(batch);
171 void RunTier2(std::vector<ProcessInfo>& vecProcs) {
172 for (
size_t i = 0; i < vecProcs.size(); ++i) {
173 if (m_bCancelled.load())
break;
175 DispatchUpdated(vecProcs[i]);
179 void ExecuteStream() {
182 std::vector<ProcessInfo> vecProcs;
188 m_bRunning.store(
false);
189 if (!m_bCancelled.load()) DispatchCompleted();
190 }
catch (
const Exception& ex) {
191 m_bRunning.store(
false);
196 void Start(
const SmartPointer<Impl>& spSelf) {
198 if (m_options.iBatchSize < 0 || m_options.iBatchIntervalMs < 0) {
199 throw ArgumentException(
"ProcessStreamOptions batch parameters cannot be negative.");
204 m_bCancelled.store(
false);
206 if (spSelf) spSelf->ExecuteStream();
208 m_spWorkerThread->Start();
213 m_bCancelled.store(
true);
214 if (m_spWorkerThread && m_spWorkerThread->IsAlive()) {
216 m_spWorkerThread->Join();
219 m_bRunning.store(
false);
235 m_pImpl->Start(m_pImpl);
239 if (m_pImpl) m_pImpl->Cancel();
243 return m_pImpl ? m_pImpl->m_bRunning.load() :
false;
Defines the exception thrown when an invalid argument is provided to a method.
Defines the exception thrown when a null reference is passed to a method that does not accept it.
Provides a re-entrant mutual exclusion primitive for thread synchronization.
Defines the exception thrown when a method call is invalid for the object's current state.
Provides an RAII-style scoped lock wrapper around synchronization primitives.
Serves as the base class for system exceptions across the library.
Creates and controls a thread, sets its priority, and gets its status mirroring .NET System....
ArgumentException(const String &sMessage)
Initializes a new instance of the ArgumentException class with a specified error message.
EventHandler< ProcessBatchEventArgs > & BatchReady
ProcessStreamOptions GetOptions() const
Gets the configuration options.
void Cancel()
Cancels background streaming.
EventHandler< ProcessEventArgs > & ProcessUpdated
void Start()
Starts background process streaming.
~ProcessStreamer() override
Destructor stopping background worker thread.
EventHandler< ProcessStreamErrorEventArgs > & Error
bool IsRunning() const
Gets whether streaming is active.
ProcessStreamer(const ProcessStreamOptions &options=ProcessStreamOptions())
Initializes a new instance of the ProcessStreamer class with options.
EventHandler< ProcessEventArgs > & ProcessDiscovered
static void EnrichProcessInfo(ProcessInfo &proc, bool bIncludeNetwork=true)
Enriches an existing ProcessInfo instance with CPU, memory, disk, and network telemetry.
static const EventArgs & Empty()
Provides a value to use with events that do not have event data.
Represents the method that will handle an event when the event provides data.
InvalidOperationException(const String &sMessage)
Initializes a new instance of the InvalidOperationException class with a specified error message.
Supports all classes in the DotNetDupe class hierarchy.
A unified smart pointer that supports both unique and shared ownership semantics.
static SmartPointer< T > NewShared()
Creates a Shared SmartPointer, default constructing T.
String()
Initializes a new instance of the String class to an empty string.
SystemException()
Initializes a new instance of the SystemException class with a default message.
Provides a re-entrant mutual exclusion primitive for thread synchronization.
static int GetCurrentThreadId()
Returns an integer identifier for the current managed thread.
static void Sleep(int millisecondsTimeout)
Suspends the current thread for the specified number of milliseconds.
static void FastPopulateProc(PROCESSENTRY32W *pe32, ProcessInfo &proc)
Populate fast tier-1 process metadata on Windows.
@ FastDiscoveryOnly
Minimal discovery retrieving PID, name, and paths without deep inspection.
static void CollectTier1Processes(std::vector< ProcessInfo > &vecProcs, int iSessionId)
Collect process snapshot using Toolhelp32 on Windows.
static void DeepEnrichProc(ProcessInfo &proc, bool bIncludeNetwork)
long long lPhysicalMemoryBytes
Working set size (physical RAM resident) for the target process.
long long lPrivateBytes
Private bytes allocated by the target process (-1 if unavailable).
Comprehensive telemetry snapshot for an operating system process.
String sName
Process executable name without path.
int iProcessId
Operating system process identifier (PID).
int iSessionId
Terminal Services session ID.
String sPath
Full filesystem path to the executable image.
MemoryInfo memory
Memory allocation statistics.
Configuration options controlling the execution of a ProcessStreamer.
ProcessStreamOptions()
Default constructor configuring progressive telemetry defaults.