Process Hacker
graph.c
Go to the documentation of this file.
1 /*
2  * Process Hacker Extra Plugins -
3  * Network Adapters Plugin
4  *
5  * Copyright (C) 2015 dmex
6  *
7  * This file is part of Process Hacker.
8  *
9  * Process Hacker is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * Process Hacker is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 #include "main.h"
24 
25 #define MSG_UPDATE (WM_APP + 1)
26 #define MSG_UPDATE_PANEL (WM_APP + 2)
27 
28 static VOID NTAPI ProcessesUpdatedHandler(
29  _In_opt_ PVOID Parameter,
30  _In_opt_ PVOID Context
31  )
32 {
33  PPH_NETADAPTER_SYSINFO_CONTEXT context = Context;
34 
35  if (context->WindowHandle)
36  {
37  PostMessage(context->WindowHandle, MSG_UPDATE, 0, 0);
38  }
39 }
40 
41 static VOID NetAdapterUpdateGraphs(
42  _Inout_ PPH_NETADAPTER_SYSINFO_CONTEXT Context
43  )
44 {
45  Context->GraphState.Valid = FALSE;
46  Context->GraphState.TooltipIndex = -1;
47  Graph_MoveGrid(Context->GraphHandle, 1);
48  Graph_Draw(Context->GraphHandle);
49  Graph_UpdateTooltip(Context->GraphHandle);
50  InvalidateRect(Context->GraphHandle, NULL, FALSE);
51 }
52 
53 static VOID NetAdapterUpdatePanel(
54  _Inout_ PPH_NETADAPTER_SYSINFO_CONTEXT Context
55  )
56 {
57  ULONG64 inOctets = 0;
58  ULONG64 outOctets = 0;
59  ULONG64 linkSpeed = 0;
60  NDIS_MEDIA_CONNECT_STATE mediaState = MediaConnectStateUnknown;
61 
62  if (Context->DeviceHandle)
63  {
64  NDIS_STATISTICS_INFO interfaceStats;
65  NDIS_LINK_STATE interfaceState;
66 
67  if (NT_SUCCESS(NetworkAdapterQueryStatistics(Context->DeviceHandle, &interfaceStats)))
68  {
69  if (!(interfaceStats.SupportedStatistics & NDIS_STATISTICS_FLAGS_VALID_BYTES_RCV))
70  inOctets = NetworkAdapterQueryValue(Context->DeviceHandle, OID_GEN_BYTES_RCV);
71  else
72  inOctets = interfaceStats.ifHCInOctets;
73 
74  if (!(interfaceStats.SupportedStatistics & NDIS_STATISTICS_FLAGS_VALID_BYTES_XMIT))
75  outOctets = NetworkAdapterQueryValue(Context->DeviceHandle, OID_GEN_BYTES_XMIT);
76  else
77  outOctets = interfaceStats.ifHCOutOctets;
78  }
79  else
80  {
81  // Note: The above code fails for some drivers that don't implement statistics (even though statistics are mandatory).
82  // NDIS handles these two OIDs for all miniport drivers and we can use these for those special cases.
83 
84  // https://msdn.microsoft.com/en-us/library/ff569443.aspx
85  inOctets = NetworkAdapterQueryValue(Context->DeviceHandle, OID_GEN_BYTES_RCV);
86 
87  // https://msdn.microsoft.com/en-us/library/ff569445.aspx
88  outOctets = NetworkAdapterQueryValue(Context->DeviceHandle, OID_GEN_BYTES_XMIT);
89  }
90 
91  if (NT_SUCCESS(NetworkAdapterQueryLinkState(Context->DeviceHandle, &interfaceState)))
92  {
93  mediaState = interfaceState.MediaConnectState;
94  linkSpeed = interfaceState.XmitLinkSpeed;
95  }
96  else
97  {
98  NetworkAdapterQueryLinkSpeed(Context->DeviceHandle, &linkSpeed);
99  }
100  }
101  else if (GetIfEntry2_I)
102  {
103  MIB_IF_ROW2 interfaceRow;
104 
105  interfaceRow = QueryInterfaceRowVista(Context);
106 
107  inOctets = interfaceRow.InOctets;
108  outOctets = interfaceRow.OutOctets;
109  mediaState = interfaceRow.MediaConnectState;
110  linkSpeed = interfaceRow.TransmitLinkSpeed;
111  }
112  else
113  {
114  MIB_IFROW interfaceRow;
115 
116  interfaceRow = QueryInterfaceRowXP(Context);
117 
118  inOctets = interfaceRow.dwInOctets;
119  outOctets = interfaceRow.dwOutOctets;
120  linkSpeed = interfaceRow.dwSpeed;
121 
122  if (interfaceRow.dwOperStatus == IF_OPER_STATUS_OPERATIONAL)
123  mediaState = MediaConnectStateConnected;
124  else
125  mediaState = MediaConnectStateDisconnected;
126  }
127 
128  if (mediaState == MediaConnectStateConnected)
129  SetDlgItemText(Context->PanelWindowHandle, IDC_LINK_STATE, L"Connected");
130  else
131  SetDlgItemText(Context->PanelWindowHandle, IDC_LINK_STATE, L"Disconnected");
132 
133  SetDlgItemText(Context->PanelWindowHandle, IDC_LINK_SPEED, PhaFormatSize(linkSpeed / BITS_IN_ONE_BYTE, -1)->Buffer);
134  SetDlgItemText(Context->PanelWindowHandle, IDC_STAT_BSENT, PhaFormatSize(outOctets, -1)->Buffer);
135  SetDlgItemText(Context->PanelWindowHandle, IDC_STAT_BRECIEVED, PhaFormatSize(inOctets, -1)->Buffer);
136  SetDlgItemText(Context->PanelWindowHandle, IDC_STAT_BTOTAL, PhaFormatSize(inOctets + outOctets, -1)->Buffer);
137 }
138 
139 static INT_PTR CALLBACK NetAdapterPanelDialogProc(
140  _In_ HWND hwndDlg,
141  _In_ UINT uMsg,
142  _In_ WPARAM wParam,
143  _In_ LPARAM lParam
144  )
145 {
146  return FALSE;
147 }
148 
149 static INT_PTR CALLBACK NetAdapterDialogProc(
150  _In_ HWND hwndDlg,
151  _In_ UINT uMsg,
152  _In_ WPARAM wParam,
153  _In_ LPARAM lParam
154  )
155 {
156  PPH_NETADAPTER_SYSINFO_CONTEXT context = NULL;
157 
158  if (uMsg == WM_INITDIALOG)
159  {
160  context = (PPH_NETADAPTER_SYSINFO_CONTEXT)lParam;
161 
162  SetProp(hwndDlg, L"Context", (HANDLE)context);
163  }
164  else
165  {
166  context = (PPH_NETADAPTER_SYSINFO_CONTEXT)GetProp(hwndDlg, L"Context");
167 
168  if (uMsg == WM_NCDESTROY)
169  {
171 
172  PhDeleteGraphState(&context->GraphState);
173 
174  if (context->GraphHandle)
175  DestroyWindow(context->GraphHandle);
176 
177  if (context->PanelWindowHandle)
178  DestroyWindow(context->PanelWindowHandle);
179 
180  PhUnregisterCallback(&PhProcessesUpdatedEvent, &context->ProcessesUpdatedRegistration);
181 
182  RemoveProp(hwndDlg, L"Context");
183  }
184  }
185 
186  if (context == NULL)
187  return FALSE;
188 
189  switch (uMsg)
190  {
191  case WM_INITDIALOG:
192  {
193  PPH_LAYOUT_ITEM graphItem;
194  PPH_LAYOUT_ITEM panelItem;
195 
196  context->WindowHandle = hwndDlg;
197 
199  PhInitializeLayoutManager(&context->LayoutManager, hwndDlg);
200 
202  graphItem = PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_GRAPH_LAYOUT), NULL, PH_ANCHOR_ALL);
203  panelItem = PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_LAYOUT), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM);
204 
205  SendMessage(GetDlgItem(hwndDlg, IDC_ADAPTERNAME), WM_SETFONT, (WPARAM)context->SysinfoSection->Parameters->LargeFont, FALSE);
206  SetDlgItemText(hwndDlg, IDC_ADAPTERNAME, context->SysinfoSection->Name.Buffer);
207 
208  context->PanelWindowHandle = CreateDialog(PluginInstance->DllBase, MAKEINTRESOURCE(IDD_NETADAPTER_PANEL), hwndDlg, NetAdapterPanelDialogProc);
209  ShowWindow(context->PanelWindowHandle, SW_SHOW);
211 
212  // Create the graph control.
213  context->GraphHandle = CreateWindow(
215  NULL,
216  WS_VISIBLE | WS_CHILD | WS_BORDER,
217  0,
218  0,
219  3,
220  3,
221  hwndDlg,
222  NULL,
223  NULL,
224  NULL
225  );
226  Graph_SetTooltip(context->GraphHandle, TRUE);
227 
228  PhAddLayoutItemEx(&context->LayoutManager, context->GraphHandle, NULL, PH_ANCHOR_ALL, graphItem->Margin);
229 
231  &PhProcessesUpdatedEvent,
232  ProcessesUpdatedHandler,
233  context,
235  );
236 
237  NetAdapterUpdateGraphs(context);
238  NetAdapterUpdatePanel(context);
239  }
240  break;
241  case WM_SIZE:
243  break;
244  case WM_NOTIFY:
245  {
246  NMHDR* header = (NMHDR*)lParam;
247 
248  if (header->hwndFrom == context->GraphHandle)
249  {
250  switch (header->code)
251  {
252  case GCN_GETDRAWINFO:
253  {
254  PPH_GRAPH_GETDRAWINFO getDrawInfo = (PPH_GRAPH_GETDRAWINFO)header;
255  PPH_GRAPH_DRAW_INFO drawInfo = getDrawInfo->DrawInfo;
256 
258  context->SysinfoSection->Parameters->ColorSetupFunction(drawInfo, PhGetIntegerSetting(L"ColorIoReadOther"), PhGetIntegerSetting(L"ColorIoWrite"));
259 
261  &context->GraphState,
262  getDrawInfo,
263  context->InboundBuffer.Count
264  );
265 
266  if (!context->GraphState.Valid)
267  {
268  ULONG i;
269  FLOAT max = 0;
270 
271  for (i = 0; i < drawInfo->LineDataCount; i++)
272  {
273  FLOAT data1;
274  FLOAT data2;
275 
276  context->GraphState.Data1[i] = data1 = (FLOAT)PhGetItemCircularBuffer_ULONG64(&context->InboundBuffer, i);
277  context->GraphState.Data2[i] = data2 = (FLOAT)PhGetItemCircularBuffer_ULONG64(&context->OutboundBuffer, i);
278 
279  if (max < data1 + data2)
280  max = data1 + data2;
281  }
282 
283  // Minimum scaling of 1 MB.
284  //if (max < 1024 * 1024)
285  // max = 1024 * 1024;
286 
287  // Scale the data.
289  context->GraphState.Data1,
290  max,
291  drawInfo->LineDataCount
292  );
293 
294  // Scale the data.
296  context->GraphState.Data2,
297  max,
298  drawInfo->LineDataCount
299  );
300 
301  context->GraphState.Valid = TRUE;
302  }
303  }
304  break;
305  case GCN_GETTOOLTIPTEXT:
306  {
307  PPH_GRAPH_GETTOOLTIPTEXT getTooltipText = (PPH_GRAPH_GETTOOLTIPTEXT)header;
308 
309  if (getTooltipText->Index < getTooltipText->TotalCount)
310  {
311  if (context->GraphState.TooltipIndex != getTooltipText->Index)
312  {
313  ULONG64 adapterInboundValue = PhGetItemCircularBuffer_ULONG64(
314  &context->InboundBuffer,
315  getTooltipText->Index
316  );
317 
318  ULONG64 adapterOutboundValue = PhGetItemCircularBuffer_ULONG64(
319  &context->OutboundBuffer,
320  getTooltipText->Index
321  );
322 
324  L"R: %s\nS: %s\n%s",
325  PhaFormatSize(adapterInboundValue, -1)->Buffer,
326  PhaFormatSize(adapterOutboundValue, -1)->Buffer,
327  ((PPH_STRING)PhAutoDereferenceObject(PhGetStatisticsTimeString(NULL, getTooltipText->Index)))->Buffer
328  ));
329  }
330 
331  getTooltipText->Text = context->GraphState.TooltipText->sr;
332  }
333  }
334  break;
335  }
336  }
337  }
338  break;
339  case MSG_UPDATE:
340  {
341  NetAdapterUpdateGraphs(context);
342  NetAdapterUpdatePanel(context);
343  }
344  break;
345  }
346 
347  return FALSE;
348 }
349 
350 static BOOLEAN NetAdapterSectionCallback(
351  _In_ PPH_SYSINFO_SECTION Section,
352  _In_ PH_SYSINFO_SECTION_MESSAGE Message,
353  _In_opt_ PVOID Parameter1,
354  _In_opt_ PVOID Parameter2
355  )
356 {
358 
359  switch (Message)
360  {
361  case SysInfoCreate:
362  {
364  {
365  // Create the handle to the network device
367  &context->DeviceHandle,
368  PhaFormatString(L"\\\\.\\%s", context->AdapterEntry->InterfaceGuid->Buffer)->Buffer,
369  FILE_GENERIC_READ,
370  FILE_ATTRIBUTE_NORMAL,
371  FILE_SHARE_READ | FILE_SHARE_WRITE,
372  FILE_OPEN,
374  );
375 
376  if (context->DeviceHandle)
377  {
378  // Check the network adapter supports the OIDs we're going to be using.
380  {
381  // Device is faulty. Close the handle so we can fallback to GetIfEntry.
382  NtClose(context->DeviceHandle);
383  context->DeviceHandle = NULL;
384  }
385  }
386  }
387 
388  PhInitializeCircularBuffer_ULONG64(&context->InboundBuffer, PhGetIntegerSetting(L"SampleCount"));
389  PhInitializeCircularBuffer_ULONG64(&context->OutboundBuffer, PhGetIntegerSetting(L"SampleCount"));
390  }
391  return TRUE;
392  case SysInfoDestroy:
393  {
394  PhDeleteCircularBuffer_ULONG64(&context->InboundBuffer);
395  PhDeleteCircularBuffer_ULONG64(&context->OutboundBuffer);
396 
397  if (context->AdapterName)
399 
400  if (context->DeviceHandle)
401  NtClose(context->DeviceHandle);
402 
403  PhFree(context);
404  }
405  return TRUE;
406  case SysInfoTick:
407  {
408  ULONG64 networkInOctets = 0;
409  ULONG64 networkOutOctets = 0;
410  ULONG64 networkRcvSpeed = 0;
411  ULONG64 networkXmitSpeed = 0;
412  //ULONG64 networkLinkSpeed = 0;
413 
414  if (context->DeviceHandle)
415  {
416  NDIS_STATISTICS_INFO interfaceStats;
417  //NDIS_LINK_STATE interfaceState;
418 
419  if (NT_SUCCESS(NetworkAdapterQueryStatistics(context->DeviceHandle, &interfaceStats)))
420  {
421  if (!(interfaceStats.SupportedStatistics & NDIS_STATISTICS_FLAGS_VALID_BYTES_RCV))
422  networkInOctets = NetworkAdapterQueryValue(context->DeviceHandle, OID_GEN_BYTES_RCV);
423  else
424  networkInOctets = interfaceStats.ifHCInOctets;
425 
426  if (!(interfaceStats.SupportedStatistics & NDIS_STATISTICS_FLAGS_VALID_BYTES_XMIT))
427  networkOutOctets = NetworkAdapterQueryValue(context->DeviceHandle, OID_GEN_BYTES_XMIT);
428  else
429  networkOutOctets = interfaceStats.ifHCOutOctets;
430 
431  networkRcvSpeed = networkInOctets - context->LastInboundValue;
432  networkXmitSpeed = networkOutOctets - context->LastOutboundValue;
433  }
434  else
435  {
436  networkInOctets = NetworkAdapterQueryValue(context->DeviceHandle, OID_GEN_BYTES_RCV);
437  networkOutOctets = NetworkAdapterQueryValue(context->DeviceHandle, OID_GEN_BYTES_XMIT);
438 
439  networkRcvSpeed = networkInOctets - context->LastInboundValue;
440  networkXmitSpeed = networkOutOctets - context->LastOutboundValue;
441  }
442 
443  //if (NT_SUCCESS(NetworkAdapterQueryLinkState(context->DeviceHandle, &interfaceState)))
444  //{
445  // networkLinkSpeed = interfaceState.XmitLinkSpeed;
446  //}
447  //else
448  //{
449  // NetworkAdapterQueryLinkSpeed(context->DeviceHandle, &networkLinkSpeed);
450  //}
451 
452  // HACK: Pull the Adapter name from the current query.
453  if (context->SysinfoSection->Name.Length == 0)
454  {
455  if (context->AdapterName = NetworkAdapterQueryName(context))
456  {
457  context->SysinfoSection->Name = context->AdapterName->sr;
458  }
459  }
460  }
461  else if (GetIfEntry2_I)
462  {
463  MIB_IF_ROW2 interfaceRow;
464 
465  interfaceRow = QueryInterfaceRowVista(context);
466 
467  networkInOctets = interfaceRow.InOctets;
468  networkOutOctets = interfaceRow.OutOctets;
469  networkRcvSpeed = networkInOctets - context->LastInboundValue;
470  networkXmitSpeed = networkOutOctets - context->LastOutboundValue;
471  //networkLinkSpeed = interfaceRow.TransmitLinkSpeed; // interfaceRow.ReceiveLinkSpeed
472 
473  // HACK: Pull the Adapter name from the current query.
474  if (context->SysinfoSection->Name.Length == 0)
475  {
476  if (context->AdapterName = PhCreateString(interfaceRow.Description))
477  {
478  context->SysinfoSection->Name = context->AdapterName->sr;
479  }
480  }
481  }
482  else
483  {
484  MIB_IFROW interfaceRow;
485 
486  interfaceRow = QueryInterfaceRowXP(context);
487 
488  networkInOctets = interfaceRow.dwInOctets;
489  networkOutOctets = interfaceRow.dwOutOctets;
490  networkRcvSpeed = networkInOctets - context->LastInboundValue;
491  networkXmitSpeed = networkOutOctets - context->LastOutboundValue;
492  //networkLinkSpeed = interfaceRow.dwSpeed;
493 
494  // HACK: Pull the Adapter name from the current query.
495  if (context->SysinfoSection->Name.Length == 0)
496  {
497  if (context->AdapterName = PhConvertMultiByteToUtf16(interfaceRow.bDescr))
498  {
499  context->SysinfoSection->Name = context->AdapterName->sr;
500  }
501  }
502  }
503 
504  if (!context->HaveFirstSample)
505  {
506  networkRcvSpeed = 0;
507  networkXmitSpeed = 0;
508  context->HaveFirstSample = TRUE;
509  }
510 
511  PhAddItemCircularBuffer_ULONG64(&context->InboundBuffer, networkRcvSpeed);
512  PhAddItemCircularBuffer_ULONG64(&context->OutboundBuffer, networkXmitSpeed);
513 
514  //context->LinkSpeed = networkLinkSpeed;
515  context->InboundValue = networkRcvSpeed;
516  context->OutboundValue = networkXmitSpeed;
517  context->LastInboundValue = networkInOctets;
518  context->LastOutboundValue = networkOutOctets;
519  }
520  return TRUE;
521  case SysInfoCreateDialog:
522  {
523  PPH_SYSINFO_CREATE_DIALOG createDialog = (PPH_SYSINFO_CREATE_DIALOG)Parameter1;
524 
525  createDialog->Instance = PluginInstance->DllBase;
526  createDialog->Template = MAKEINTRESOURCE(IDD_NETADAPTER_DIALOG);
527  createDialog->DialogProc = NetAdapterDialogProc;
528  createDialog->Parameter = context;
529  }
530  return TRUE;
532  {
533  PPH_GRAPH_DRAW_INFO drawInfo = (PPH_GRAPH_DRAW_INFO)Parameter1;
534 
536  Section->Parameters->ColorSetupFunction(drawInfo, PhGetIntegerSetting(L"ColorIoReadOther"), PhGetIntegerSetting(L"ColorIoWrite"));
537  PhGetDrawInfoGraphBuffers(&Section->GraphState.Buffers, drawInfo, context->InboundBuffer.Count);
538 
539  if (!Section->GraphState.Valid)
540  {
541  FLOAT max = 0;
542 
543  for (ULONG i = 0; i < drawInfo->LineDataCount; i++)
544  {
545  FLOAT data1;
546  FLOAT data2;
547 
548  Section->GraphState.Data1[i] = data1 = (FLOAT)PhGetItemCircularBuffer_ULONG64(&context->InboundBuffer, i);
549  Section->GraphState.Data2[i] = data2 = (FLOAT)PhGetItemCircularBuffer_ULONG64(&context->OutboundBuffer, i);
550 
551  if (max < data1 + data2)
552  max = data1 + data2;
553  }
554 
555  // Minimum scaling of 1 MB.
556  //if (max < 1024 * 1024)
557  // max = 1024 * 1024;
558 
559  // Scale the data.
561  Section->GraphState.Data1,
562  max,
563  drawInfo->LineDataCount
564  );
565 
566  // Scale the data.
568  Section->GraphState.Data2,
569  max,
570  drawInfo->LineDataCount
571  );
572  Section->GraphState.Valid = TRUE;
573  }
574  }
575  return TRUE;
577  {
579 
580  ULONG64 adapterInboundValue = PhGetItemCircularBuffer_ULONG64(
581  &context->InboundBuffer,
582  getTooltipText->Index
583  );
584 
585  ULONG64 adapterOutboundValue = PhGetItemCircularBuffer_ULONG64(
586  &context->OutboundBuffer,
587  getTooltipText->Index
588  );
589 
590  PhMoveReference(&Section->GraphState.TooltipText, PhFormatString(
591  L"R: %s\nS: %s\n%s",
592  PhaFormatSize(adapterInboundValue, -1)->Buffer,
593  PhaFormatSize(adapterOutboundValue, -1)->Buffer,
594  ((PPH_STRING)PhAutoDereferenceObject(PhGetStatisticsTimeString(NULL, getTooltipText->Index)))->Buffer
595  ));
596 
597  getTooltipText->Text = Section->GraphState.TooltipText->sr;
598  }
599  return TRUE;
601  {
602  PPH_SYSINFO_DRAW_PANEL drawPanel = (PPH_SYSINFO_DRAW_PANEL)Parameter1;
603 
604  drawPanel->Title = PhCreateString(Section->Name.Buffer);
605  drawPanel->SubTitle = PhFormatString(
606  L"R: %s\nS: %s",
607  PhaFormatSize(context->InboundValue, -1)->Buffer,
608  PhaFormatSize(context->OutboundValue, -1)->Buffer
609  );
610  }
611  return TRUE;
612  }
613 
614  return FALSE;
615 }
616 
618  _In_ PPH_PLUGIN_SYSINFO_POINTERS Pointers,
619  _In_ PPH_NETADAPTER_ENTRY AdapterEntry
620  )
621 {
622  PH_SYSINFO_SECTION section;
624 
625  context = (PPH_NETADAPTER_SYSINFO_CONTEXT)PhAllocate(sizeof(PH_NETADAPTER_SYSINFO_CONTEXT));
626  memset(context, 0, sizeof(PH_NETADAPTER_SYSINFO_CONTEXT));
627  memset(&section, 0, sizeof(PH_SYSINFO_SECTION));
628 
629  context->AdapterEntry = AdapterEntry;
630 
631  section.Context = context;
632  section.Callback = NetAdapterSectionCallback;
633 
634  PhInitializeStringRef(&section.Name, L"");
635 
636  context->SysinfoSection = Pointers->CreateSection(&section);
637 }