Process Hacker
ping.c
Go to the documentation of this file.
1 /*
2  * Process Hacker Network Tools -
3  * Ping dialog
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 "nettools.h"
24 
25 #define WM_PING_UPDATE (WM_APP + 151)
26 
27 static RECT NormalGraphTextMargin = { 5, 5, 5, 5 };
28 static RECT NormalGraphTextPadding = { 3, 3, 3, 3 };
29 
30 static HFONT InitializeFont(
31  _In_ HWND hwnd
32  )
33 {
34  HFONT fontHandle;
35  NONCLIENTMETRICS metrics = { sizeof(NONCLIENTMETRICS) };
36 
37  if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &metrics, 0))
38  {
39  metrics.lfMessageFont.lfHeight = -15;
40  //metrics.lfMessageFont.lfWeight = FW_MEDIUM;
41  //metrics.lfMessageFont.lfQuality = CLEARTYPE_QUALITY | ANTIALIASED_QUALITY;
42 
43  fontHandle = CreateFontIndirect(&metrics.lfMessageFont);
44  }
45  else
46  {
47  LOGFONT font;
48 
49  GetObject((HFONT)GetStockObject(DEFAULT_GUI_FONT), sizeof(LOGFONT), &font);
50 
51  font.lfHeight = -15;
52  font.lfWeight = FW_MEDIUM;
53 
54  fontHandle = CreateFontIndirect(&font);
55  }
56 
57  SendMessage(hwnd, WM_SETFONT, (WPARAM)fontHandle, TRUE);
58 
59  return fontHandle;
60 }
61 
62 static VOID PhNetworkPingUpdateGraph(
63  _In_ PNETWORK_OUTPUT_CONTEXT Context
64  )
65 {
66  Context->PingGraphState.Valid = FALSE;
67  Context->PingGraphState.TooltipIndex = -1;
68  Graph_MoveGrid(Context->PingGraphHandle, 1);
69  Graph_Draw(Context->PingGraphHandle);
70  Graph_UpdateTooltip(Context->PingGraphHandle);
71  InvalidateRect(Context->PingGraphHandle, NULL, FALSE);
72 }
73 
80 static PPH_BYTES PhFormatAnsiString_V(
81  _In_ _Printf_format_string_ PSTR Format,
82  _In_ va_list ArgPtr
83  )
84 {
85  PPH_BYTES string;
86  int length;
87 
88  length = _vscprintf(Format, ArgPtr);
89 
90  if (length == -1)
91  return NULL;
92 
93  string = PhCreateBytesEx(NULL, length * sizeof(CHAR));
94 
95  _vsnprintf(
96  string->Buffer,
97  length,
98  Format, ArgPtr
99  );
100 
101  return string;
102 }
103 
109 static PPH_BYTES PhFormatAnsiString(
110  _In_ _Printf_format_string_ PSTR Format,
111  ...
112  )
113 {
114  va_list argptr;
115 
116  va_start(argptr, Format);
117 
118  return PhFormatAnsiString_V(Format, argptr);
119 }
120 
121 static ULONG PhNetworkPingThreadStart(
122  _In_ PVOID Parameter
123  )
124 {
125  HANDLE icmpHandle = INVALID_HANDLE_VALUE;
126  ULONG icmpCurrentPingMs = 0;
127  ULONG icmpCurrentPingTtl = 0;
128  ULONG icmpReplyCount = 0;
129  ULONG icmpReplyLength = 0;
130  PVOID icmpReplyBuffer = NULL;
131  PPH_STRING phVersion = NULL;
132  PPH_BYTES icmpEchoBuffer = NULL;
133  IP_OPTION_INFORMATION pingOptions =
134  {
135  255, // Time To Live
136  0, // Type Of Service
137  IP_FLAG_DF, // IP header flags
138  0 // Size of options data
139  };
140 
142 
143  __try
144  {
145  // Query PH version.
146  if ((phVersion = PhGetPhVersion()) == NULL)
147  __leave;
148 
149  // Create ICMP echo buffer.
150  if ((icmpEchoBuffer = PhFormatAnsiString("processhacker_%S_0x0D06F00D_x1", phVersion->Buffer)) == NULL)
151  __leave;
152 
153  if (context->IpAddress.Type == PH_IPV6_NETWORK_TYPE)
154  {
155  SOCKADDR_IN6 icmp6LocalAddr = { 0 };
156  SOCKADDR_IN6 icmp6RemoteAddr = { 0 };
157  PICMPV6_ECHO_REPLY icmp6ReplyStruct = NULL;
158 
159  // Create ICMPv6 handle.
160  if ((icmpHandle = Icmp6CreateFile()) == INVALID_HANDLE_VALUE)
161  __leave;
162 
163  // Set Local IPv6-ANY address.
164  icmp6LocalAddr.sin6_addr = in6addr_any;
165  icmp6LocalAddr.sin6_family = AF_INET6;
166 
167  // Set Remote IPv6 address.
168  icmp6RemoteAddr.sin6_addr = context->IpAddress.In6Addr;
169  icmp6RemoteAddr.sin6_port = _byteswap_ushort((USHORT)context->NetworkItem->RemoteEndpoint.Port);
170 
171  // Allocate ICMPv6 message.
172  icmpReplyLength = ICMP_BUFFER_SIZE(sizeof(ICMPV6_ECHO_REPLY), icmpEchoBuffer);
173  icmpReplyBuffer = PhAllocate(icmpReplyLength);
174  memset(icmpReplyBuffer, 0, icmpReplyLength);
175 
176  InterlockedIncrement(&context->PingSentCount);
177 
178  // Send ICMPv6 ping...
179  icmpReplyCount = Icmp6SendEcho2(
180  icmpHandle,
181  NULL,
182  NULL,
183  NULL,
184  &icmp6LocalAddr,
185  &icmp6RemoteAddr,
186  icmpEchoBuffer->Buffer,
187  (USHORT)icmpEchoBuffer->Length,
188  &pingOptions,
189  icmpReplyBuffer,
190  icmpReplyLength,
191  context->MaxPingTimeout
192  );
193 
194  icmp6ReplyStruct = (PICMPV6_ECHO_REPLY)icmpReplyBuffer;
195  if (icmpReplyCount > 0 && icmp6ReplyStruct)
196  {
197  BOOLEAN icmpPacketSignature = FALSE;
198 
199  if (icmp6ReplyStruct->Status != IP_SUCCESS)
200  {
201  InterlockedIncrement(&context->PingLossCount);
202  }
203 
204  if (_memicmp(
205  icmp6ReplyStruct->Address.sin6_addr,
206  context->IpAddress.In6Addr.u.Word,
207  sizeof(icmp6ReplyStruct->Address.sin6_addr)
208  ) != 0)
209  {
210  InterlockedIncrement(&context->UnknownAddrCount);
211  }
212 
213  //if (icmp6ReplyStruct->DataSize == icmpEchoBuffer->MaximumLength)
214  //{
215  // icmpPacketSignature = (_memicmp(
216  // icmpEchoBuffer->Buffer,
217  // icmp6ReplyStruct->Data,
218  // icmp6ReplyStruct->DataSize
219  // ) == 0);
220  //}
221 
222  //if (icmpPacketSignature != TRUE)
223  //{
224  // InterlockedIncrement(&context->HashFailCount);
225  //}
226 
227  icmpCurrentPingMs = icmp6ReplyStruct->RoundTripTime;
228  //icmpCurrentPingTtl = icmp6ReplyStruct->Options.Ttl;
229  }
230  else
231  {
232  InterlockedIncrement(&context->PingLossCount);
233  }
234  }
235  else
236  {
237  IPAddr icmpLocalAddr = 0;
238  IPAddr icmpRemoteAddr = 0;
239  PICMP_ECHO_REPLY icmpReplyStruct = NULL;
240 
241  // Create ICMPv4 handle.
242  if ((icmpHandle = IcmpCreateFile()) == INVALID_HANDLE_VALUE)
243  __leave;
244 
245  // Set Local IPv4-ANY address.
246  icmpLocalAddr = in4addr_any.s_addr;
247 
248  // Set Remote IPv4 address.
249  icmpRemoteAddr = context->IpAddress.InAddr.s_addr;
250 
251  // Allocate ICMPv4 message.
252  icmpReplyLength = ICMP_BUFFER_SIZE(sizeof(ICMP_ECHO_REPLY), icmpEchoBuffer);
253  icmpReplyBuffer = PhAllocate(icmpReplyLength);
254  memset(icmpReplyBuffer, 0, icmpReplyLength);
255 
256  InterlockedIncrement(&context->PingSentCount);
257 
258  // Send ICMPv4 ping...
259  //if (WindowsVersion > WINDOWS_VISTA)
260  //{
261  // // Vista SP1 and up we can specify the source address:
262  // icmpReplyCount = IcmpSendEcho2Ex(
263  // icmpHandle,
264  // NULL,
265  // NULL,
266  // NULL,
267  // icmpLocalAddr,
268  // icmpRemoteAddr,
269  // icmpEchoBuffer->Buffer,
270  // icmpEchoBuffer->MaximumLength,
271  // &pingOptions,
272  // icmpReplyBuffer,
273  // icmpReplyLength,
274  // context->MaxPingTimeout
275  // );
276  //}
277 
278  icmpReplyCount = IcmpSendEcho2(
279  icmpHandle,
280  NULL,
281  NULL,
282  NULL,
283  icmpRemoteAddr,
284  icmpEchoBuffer->Buffer,
285  (USHORT)icmpEchoBuffer->Length,
286  &pingOptions,
287  icmpReplyBuffer,
288  icmpReplyLength,
289  context->MaxPingTimeout
290  );
291 
292  icmpReplyStruct = (PICMP_ECHO_REPLY)icmpReplyBuffer;
293 
294  if (icmpReplyStruct && icmpReplyCount > 0)
295  {
296  BOOLEAN icmpPacketSignature = FALSE;
297 
298  if (icmpReplyStruct->Status != IP_SUCCESS)
299  {
300  InterlockedIncrement(&context->PingLossCount);
301  }
302 
303  if (icmpReplyStruct->Address != context->IpAddress.InAddr.s_addr)
304  {
305  InterlockedIncrement(&context->UnknownAddrCount);
306  }
307 
308  if (icmpReplyStruct->DataSize == icmpEchoBuffer->Length)
309  {
310  icmpPacketSignature = (_memicmp(
311  icmpEchoBuffer->Buffer,
312  icmpReplyStruct->Data,
313  icmpReplyStruct->DataSize
314  ) == 0);
315  }
316 
317  icmpCurrentPingMs = icmpReplyStruct->RoundTripTime;
318  icmpCurrentPingTtl = icmpReplyStruct->Options.Ttl;
319 
320  if (!icmpPacketSignature)
321  {
322  InterlockedIncrement(&context->HashFailCount);
323  }
324  }
325  else
326  {
327  InterlockedIncrement(&context->PingLossCount);
328  }
329  }
330 
331  InterlockedIncrement(&context->PingRecvCount);
332 
333  if (context->PingMinMs == 0 || icmpCurrentPingMs < context->PingMinMs)
334  context->PingMinMs = icmpCurrentPingMs;
335  if (icmpCurrentPingMs > context->PingMaxMs)
336  context->PingMaxMs = icmpCurrentPingMs;
337 
338  context->CurrentPingMs = icmpCurrentPingMs;
339 
340  PhAddItemCircularBuffer_ULONG(&context->PingHistory, icmpCurrentPingMs);
341  }
342  __finally
343  {
344  if (phVersion)
345  {
346  PhDereferenceObject(phVersion);
347  }
348 
349  if (icmpEchoBuffer)
350  {
351  PhDereferenceObject(icmpEchoBuffer);
352  }
353 
354  if (icmpHandle != INVALID_HANDLE_VALUE)
355  {
356  IcmpCloseHandle(icmpHandle);
357  }
358 
359  if (icmpReplyBuffer)
360  {
361  PhFree(icmpReplyBuffer);
362  }
363  }
364 
365  PostMessage(context->WindowHandle, WM_PING_UPDATE, 0, 0);
366 
367  return STATUS_SUCCESS;
368 }
369 
370 static VOID NTAPI NetworkPingUpdateHandler(
371  _In_opt_ PVOID Parameter,
372  _In_opt_ PVOID Context
373  )
374 {
376 
377  // Queue up the next ping into our work queue...
379  &context->PingWorkQueue,
380  PhNetworkPingThreadStart,
381  (PVOID)context
382  );
383 }
384 
385 static INT_PTR CALLBACK NetworkPingWndProc(
386  _In_ HWND hwndDlg,
387  _In_ UINT uMsg,
388  _In_ WPARAM wParam,
389  _In_ LPARAM lParam
390  )
391 {
392  PNETWORK_OUTPUT_CONTEXT context = NULL;
393 
394  if (uMsg == WM_INITDIALOG)
395  {
396  context = (PNETWORK_OUTPUT_CONTEXT)lParam;
397  SetProp(hwndDlg, L"Context", (HANDLE)context);
398  }
399  else
400  {
401  context = (PNETWORK_OUTPUT_CONTEXT)GetProp(hwndDlg, L"Context");
402  }
403 
404  if (context == NULL)
405  return FALSE;
406 
407  switch (uMsg)
408  {
409  case WM_INITDIALOG:
410  {
411  PH_RECTANGLE windowRectangle;
412  PPH_LAYOUT_ITEM panelItem;
413 
414  // We have already set the group boxes to have WS_EX_TRANSPARENT to fix
415  // the drawing issue that arises when using WS_CLIPCHILDREN. However
416  // in removing the flicker from the graphs the group boxes will now flicker.
417  // It's a good tradeoff since no one stares at the group boxes.
418  PhSetWindowStyle(hwndDlg, WS_CLIPCHILDREN, WS_CLIPCHILDREN);
419 
420  context->WindowHandle = hwndDlg;
421  context->ParentHandle = GetParent(hwndDlg);
422  context->StatusHandle = GetDlgItem(hwndDlg, IDC_MAINTEXT);
424 
427 
428  // Create the font handle.
429  context->FontHandle = InitializeFont(context->StatusHandle);
430 
431  // Create the graph control.
432  context->PingGraphHandle = CreateWindow(
434  NULL,
435  WS_VISIBLE | WS_CHILD | WS_BORDER,
436  0,
437  0,
438  3,
439  3,
440  hwndDlg,
441  NULL,
442  NULL,
443  NULL
444  );
446 
447  // Load the Process Hacker icon.
448  context->IconHandle = (HICON)LoadImage(
449  GetModuleHandle(NULL),
450  MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER),
451  IMAGE_ICON,
452  GetSystemMetrics(SM_CXICON),
453  GetSystemMetrics(SM_CYICON),
454  LR_SHARED
455  );
456  // Set window icon.
457  if (context->IconHandle)
458  SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)context->IconHandle);
459 
460  // Initialize the WorkQueue with a maximum of 20 threads (fix pinging slow-links with a high interval update).
461  PhInitializeWorkQueue(&context->PingWorkQueue, 0, 20, 5000);
463  PhInitializeLayoutManager(&context->LayoutManager, hwndDlg);
464  PhInitializeCircularBuffer_ULONG(&context->PingHistory, PhGetIntegerSetting(L"SampleCount"));
465 
466  PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_ICMP_PANEL), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT);
467  PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_ICMP_AVG), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT);
468  PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_ICMP_MIN), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT);
469  PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_ICMP_MAX), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT);
470  PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_PINGS_SENT), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT);
471  PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_PINGS_LOST), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT);
472  PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_BAD_HASH), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT);
473  PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_ANON_ADDR), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT);
474  PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDOK), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_RIGHT);
475  panelItem = PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_PING_LAYOUT), NULL, PH_ANCHOR_ALL);
476  PhAddLayoutItemEx(&context->LayoutManager, context->PingGraphHandle, NULL, PH_ANCHOR_ALL, panelItem->Margin);
477 
478  // Load window settings.
479  if (windowRectangle.Position.X == 0 || windowRectangle.Position.Y == 0)
480  PhCenterWindow(hwndDlg, GetParent(hwndDlg));
481  else
482  {
484  }
485 
486  // Initialize window layout.
488 
489  // Convert IP Address to string format.
490  if (context->IpAddress.Type == PH_IPV4_NETWORK_TYPE)
491  {
493  }
494  else
495  {
497  }
498 
499  SetWindowText(hwndDlg, PhaFormatString(L"Ping %s", context->IpAddressString)->Buffer);
500  SetWindowText(context->StatusHandle, PhaFormatString(L"Pinging %s with 32 bytes of data:", context->IpAddressString)->Buffer);
501 
504  NetworkPingUpdateHandler,
505  context,
507  );
508  }
509  return TRUE;
510  case WM_COMMAND:
511  {
512  switch (LOWORD(wParam))
513  {
514  case IDCANCEL:
515  case IDOK:
516  PostQuitMessage(0);
517  break;
518  }
519  }
520  break;
521  case WM_DESTROY:
522  {
526  );
527 
531  hwndDlg
532  );
533 
534  if (context->PingGraphHandle)
535  DestroyWindow(context->PingGraphHandle);
536 
537  if (context->IconHandle)
538  DestroyIcon(context->IconHandle);
539 
540  if (context->FontHandle)
541  DeleteObject(context->FontHandle);
542 
543  PhDeleteWorkQueue(&context->PingWorkQueue);
546 
547  RemoveProp(hwndDlg, L"Context");
548  PhFree(context);
549  }
550  break;
551  case WM_SIZE:
553  break;
554  case WM_SIZING:
555  PhResizingMinimumSize((PRECT)lParam, wParam, 420, 250);
556  break;
557  case WM_CTLCOLORBTN:
558  case WM_CTLCOLORDLG:
559  case WM_CTLCOLORSTATIC:
560  {
561  HDC hDC = (HDC)wParam;
562  HWND hwndChild = (HWND)lParam;
563 
564  // Check for our static label and change the color.
565  if (GetDlgCtrlID(hwndChild) == IDC_MAINTEXT)
566  {
567  SetTextColor(hDC, RGB(19, 112, 171));
568  }
569 
570  // Set a transparent background for the control backcolor.
571  SetBkMode(hDC, TRANSPARENT);
572 
573  // set window background color.
574  return (INT_PTR)GetSysColorBrush(COLOR_WINDOW);
575  }
576  break;
577  case WM_PING_UPDATE:
578  {
579  ULONG i = 0;
580  ULONG maxGraphHeight = 0;
581  ULONG pingAvgValue = 0;
582 
583  PhNetworkPingUpdateGraph(context);
584 
585  for (i = 0; i < context->PingHistory.Count; i++)
586  {
587  maxGraphHeight = maxGraphHeight + PhGetItemCircularBuffer_ULONG(&context->PingHistory, i);
588  pingAvgValue = maxGraphHeight / context->PingHistory.Count;
589  }
590 
591  SetDlgItemText(hwndDlg, IDC_ICMP_AVG, PhaFormatString(
592  L"Average: %lums", pingAvgValue)->Buffer);
593  SetDlgItemText(hwndDlg, IDC_ICMP_MIN, PhaFormatString(
594  L"Minimum: %lums", context->PingMinMs)->Buffer);
595  SetDlgItemText(hwndDlg, IDC_ICMP_MAX, PhaFormatString(
596  L"Maximum: %lums", context->PingMaxMs)->Buffer);
597 
598  SetDlgItemText(hwndDlg, IDC_PINGS_SENT, PhaFormatString(
599  L"Pings Sent: %lu", context->PingSentCount)->Buffer);
600  SetDlgItemText(hwndDlg, IDC_PINGS_LOST, PhaFormatString(
601  L"Pings Lost: %lu (%.0f%%)", context->PingLossCount,
602  ((FLOAT)context->PingLossCount / context->PingSentCount * 100)
603  )->Buffer);
604 
605  SetDlgItemText(hwndDlg, IDC_BAD_HASH, PhaFormatString(
606  L"Bad Hashes: %lu", context->HashFailCount)->Buffer);
607  SetDlgItemText(hwndDlg, IDC_ANON_ADDR, PhaFormatString(
608  L"Anon Replies: %lu", context->UnknownAddrCount)->Buffer);
609  }
610  break;
611  case WM_NOTIFY:
612  {
613  LPNMHDR header = (LPNMHDR)lParam;
614 
615  switch (header->code)
616  {
617  case GCN_GETDRAWINFO:
618  {
619  PPH_GRAPH_GETDRAWINFO getDrawInfo = (PPH_GRAPH_GETDRAWINFO)header;
620  PPH_GRAPH_DRAW_INFO drawInfo = getDrawInfo->DrawInfo;
621 
622  PhSiSetColorsGraphDrawInfo(drawInfo, PhGetIntegerSetting(L"ColorCpuKernel"), PhGetIntegerSetting(L"ColorCpuUser"));
623 
624  if (header->hwndFrom == context->PingGraphHandle)
625  {
626  if (PhGetIntegerSetting(L"GraphShowText"))
627  {
628  HDC hdc = Graph_GetBufferedContext(context->PingGraphHandle);
629 
631  PhFormatString(L"Ping: %ums", context->CurrentPingMs)
632  );
633 
634  SelectObject(hdc, PhApplicationFont);
635  PhSetGraphText(hdc, drawInfo, &context->PingGraphState.Text->sr,
636  &NormalGraphTextMargin, &NormalGraphTextPadding, PH_ALIGN_TOP | PH_ALIGN_LEFT);
637  }
638  else
639  {
640  drawInfo->Text.Buffer = NULL;
641  }
642 
644  &context->PingGraphState,
645  getDrawInfo,
646  context->PingHistory.Count
647  );
648 
649  if (!context->PingGraphState.Valid)
650  {
651  ULONG i;
652  FLOAT max = 0;
653 
654  for (i = 0; i < drawInfo->LineDataCount; i++)
655  {
656  FLOAT data1;
657 
658  context->PingGraphState.Data1[i] = data1 = (FLOAT)PhGetItemCircularBuffer_ULONG(&context->PingHistory, i);
659 
660  if (max < data1)
661  max = data1;
662  }
663 
664  // Minimum scaling of timeout (1000ms default).
665  if (max < (FLOAT)context->MaxPingTimeout)
666  max = (FLOAT)context->MaxPingTimeout;
667 
668  // Scale the data.
670  context->PingGraphState.Data1,
671  max,
672  drawInfo->LineDataCount
673  );
674 
675  context->PingGraphState.Valid = TRUE;
676  }
677  }
678  }
679  break;
680  case GCN_GETTOOLTIPTEXT:
681  {
682  PPH_GRAPH_GETTOOLTIPTEXT getTooltipText = (PPH_GRAPH_GETTOOLTIPTEXT)lParam;
683 
684  if (getTooltipText->Index < getTooltipText->TotalCount)
685  {
686  if (header->hwndFrom == context->PingGraphHandle)
687  {
688  if (context->PingGraphState.TooltipIndex != getTooltipText->Index)
689  {
690  ULONG pingMs = PhGetItemCircularBuffer_ULONG(&context->PingHistory, getTooltipText->Index);
691 
693  PhFormatString(L"Ping: %ums", pingMs)
694  );
695  }
696 
697  getTooltipText->Text = context->PingGraphState.TooltipText->sr;
698  }
699  }
700  }
701  break;
702  }
703  }
704  break;
705  }
706 
707  return FALSE;
708 }
709 
711  _In_ PVOID Parameter
712  )
713 {
714  BOOL result;
715  MSG message;
716  HWND windowHandle;
717  PH_AUTO_POOL autoPool;
719 
720  PhInitializeAutoPool(&autoPool);
721 
722  windowHandle = CreateDialogParam(
723  (HINSTANCE)PluginInstance->DllBase,
724  MAKEINTRESOURCE(IDD_PINGDIALOG),
726  NetworkPingWndProc,
727  (LPARAM)Parameter
728  );
729 
730  ShowWindow(windowHandle, SW_SHOW);
731  SetForegroundWindow(windowHandle);
732 
733  while (result = GetMessage(&message, NULL, 0, 0))
734  {
735  if (result == -1)
736  break;
737 
738  if (!IsDialogMessage(windowHandle, &message))
739  {
740  TranslateMessage(&message);
741  DispatchMessage(&message);
742  }
743 
744  PhDrainAutoPool(&autoPool);
745  }
746 
747  PhDeleteAutoPool(&autoPool);
748  DestroyWindow(windowHandle);
749 
750  return STATUS_SUCCESS;
751 }