1 /*
2  * PROJECT:         ReactOS Event Log Viewer
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            base/applications/mscutils/eventvwr/evtdetctl.c
5  * PURPOSE:         Event Details Control
6  * PROGRAMMERS:     Marc Piulachs (marc.piulachs at codexchange [dot] net)
7  *                  Eric Kohl
8  *                  Hermes Belusca-Maito
9  */
10 
11 #include "eventvwr.h"
12 #include "evtdetctl.h"
13 
14 #include <shellapi.h>
15 
16 // FIXME:
17 #define EVENT_MESSAGE_EVENTTEXT_BUFFER  1024*10
18 extern HWND hwndListView;
19 extern BOOL
20 GetEventMessage(IN LPCWSTR KeyName,
21                 IN LPCWSTR SourceName,
22                 IN PEVENTLOGRECORD pevlr,
23                 OUT PWCHAR EventText);
24 
25 
26 typedef struct _DETAILDATA
27 {
28     PEVENTLOGFILTER EventLogFilter;
29 
30     BOOL bDisplayWords;
31     HFONT hMonospaceFont;
32 
33     INT cxMin, cyMin;
34     INT cxOld, cyOld;
35     POINT scPos;
36 } DETAILDATA, *PDETAILDATA;
37 
38 
39 static
40 VOID
41 DisplayEvent(HWND hDlg, PEVENTLOGFILTER EventLogFilter)
42 {
43     WCHAR szEventType[MAX_PATH];
44     WCHAR szTime[MAX_PATH];
45     WCHAR szDate[MAX_PATH];
46     WCHAR szUser[MAX_PATH];
47     WCHAR szComputer[MAX_PATH];
48     WCHAR szSource[MAX_PATH];
49     WCHAR szCategory[MAX_PATH];
50     WCHAR szEventID[MAX_PATH];
51     WCHAR szEventText[EVENT_MESSAGE_EVENTTEXT_BUFFER];
52     BOOL bEventData = FALSE;
53     LVITEMW li;
54     PEVENTLOGRECORD pevlr;
55     int iIndex;
56 
57     /* Get index of selected item */
58     iIndex = ListView_GetNextItem(hwndListView, -1, LVNI_SELECTED | LVNI_FOCUSED);
59     if (iIndex == -1)
60     {
61         MessageBoxW(hDlg,
62                     L"No Items in ListView",
63                     L"Error",
64                     MB_OK | MB_ICONINFORMATION);
65         return;
66     }
67 
68     li.mask = LVIF_PARAM;
69     li.iItem = iIndex;
70     li.iSubItem = 0;
71 
72     ListView_GetItem(hwndListView, &li);
73 
74     pevlr = (PEVENTLOGRECORD)li.lParam;
75 
76     ListView_GetItemText(hwndListView, iIndex, 0, szEventType, ARRAYSIZE(szEventType));
77     ListView_GetItemText(hwndListView, iIndex, 1, szDate, ARRAYSIZE(szDate));
78     ListView_GetItemText(hwndListView, iIndex, 2, szTime, ARRAYSIZE(szTime));
79     ListView_GetItemText(hwndListView, iIndex, 3, szSource, ARRAYSIZE(szSource));
80     ListView_GetItemText(hwndListView, iIndex, 4, szCategory, ARRAYSIZE(szCategory));
81     ListView_GetItemText(hwndListView, iIndex, 5, szEventID, ARRAYSIZE(szEventID));
82     ListView_GetItemText(hwndListView, iIndex, 6, szUser, ARRAYSIZE(szUser));
83     ListView_GetItemText(hwndListView, iIndex, 7, szComputer, ARRAYSIZE(szComputer));
84 
85     SetDlgItemTextW(hDlg, IDC_EVENTDATESTATIC, szDate);
86     SetDlgItemTextW(hDlg, IDC_EVENTTIMESTATIC, szTime);
87     SetDlgItemTextW(hDlg, IDC_EVENTUSERSTATIC, szUser);
88     SetDlgItemTextW(hDlg, IDC_EVENTSOURCESTATIC, szSource);
89     SetDlgItemTextW(hDlg, IDC_EVENTCOMPUTERSTATIC, szComputer);
90     SetDlgItemTextW(hDlg, IDC_EVENTCATEGORYSTATIC, szCategory);
91     SetDlgItemTextW(hDlg, IDC_EVENTIDSTATIC, szEventID);
92     SetDlgItemTextW(hDlg, IDC_EVENTTYPESTATIC, szEventType);
93 
94     bEventData = (pevlr->DataLength > 0);
95     EnableDlgItem(hDlg, IDC_BYTESRADIO, bEventData);
96     EnableDlgItem(hDlg, IDC_WORDRADIO, bEventData);
97 
98     // FIXME: At the moment we support only one event log in the filter
99     GetEventMessage(EventLogFilter->EventLogs[0]->LogName, szSource, pevlr, szEventText);
100     SetDlgItemTextW(hDlg, IDC_EVENTTEXTEDIT, szEventText);
101 }
102 
103 static
104 UINT
105 PrintByteDataLine(PWCHAR pBuffer, UINT uOffset, PBYTE pData, UINT uLength)
106 {
107     PWCHAR p = pBuffer;
108     UINT n, i, r = 0;
109 
110     if (uOffset != 0)
111     {
112         n = swprintf(p, L"\r\n");
113         p += n;
114         r += n;
115     }
116 
117     n = swprintf(p, L"%04lx:", uOffset);
118     p += n;
119     r += n;
120 
121     for (i = 0; i < uLength; i++)
122     {
123         n = swprintf(p, L" %02x", pData[i]);
124         p += n;
125         r += n;
126     }
127 
128     for (i = 0; i < 9 - uLength; i++)
129     {
130         n = swprintf(p, L"   ");
131         p += n;
132         r += n;
133     }
134 
135     for (i = 0; i < uLength; i++)
136     {
137         // NOTE: Normally iswprint should return FALSE for tabs...
138         n = swprintf(p, L"%c", (iswprint(pData[i]) && (pData[i] != L'\t')) ? pData[i] : L'.');
139         p += n;
140         r += n;
141     }
142 
143     return r;
144 }
145 
146 static
147 UINT
148 PrintWordDataLine(PWCHAR pBuffer, UINT uOffset, PULONG pData, UINT uLength)
149 {
150     PWCHAR p = pBuffer;
151     UINT n, i, r = 0;
152 
153     if (uOffset != 0)
154     {
155         n = swprintf(p, L"\r\n");
156         p += n;
157         r += n;
158     }
159 
160     n = swprintf(p, L"%04lx:", uOffset);
161     p += n;
162     r += n;
163 
164     for (i = 0; i < uLength / sizeof(ULONG); i++)
165     {
166         n = swprintf(p, L" %08lx", pData[i]);
167         p += n;
168         r += n;
169     }
170 
171     /* Display the remaining bytes if uLength was not a multiple of sizeof(ULONG) */
172     for (i = (uLength / sizeof(ULONG)) * sizeof(ULONG); i < uLength; i++)
173     {
174         n = swprintf(p, L" %02x", ((PBYTE)pData)[i]);
175         p += n;
176         r += n;
177     }
178 
179     return r;
180 }
181 
182 static
183 VOID
184 DisplayEventData(HWND hDlg, BOOL bDisplayWords)
185 {
186     LVITEMW li;
187     PEVENTLOGRECORD pevlr;
188     int iIndex;
189 
190     LPBYTE pData;
191     UINT i, uOffset;
192     UINT uBufferSize, uLineLength;
193     PWCHAR pTextBuffer, pLine;
194 
195     /* Get index of selected item */
196     iIndex = ListView_GetNextItem(hwndListView, -1, LVNI_SELECTED | LVNI_FOCUSED);
197     if (iIndex == -1)
198     {
199         MessageBoxW(hDlg,
200                     L"No Items in ListView",
201                     L"Error",
202                     MB_OK | MB_ICONINFORMATION);
203         return;
204     }
205 
206     li.mask = LVIF_PARAM;
207     li.iItem = iIndex;
208     li.iSubItem = 0;
209 
210     ListView_GetItem(hwndListView, &li);
211 
212     pevlr = (PEVENTLOGRECORD)li.lParam;
213     if (pevlr->DataLength == 0)
214     {
215         SetDlgItemTextW(hDlg, IDC_EVENTDATAEDIT, L"");
216         return;
217     }
218 
219     if (bDisplayWords)
220         uBufferSize = ((pevlr->DataLength / 8) + 1) * 26 * sizeof(WCHAR);
221     else
222         uBufferSize = ((pevlr->DataLength / 8) + 1) * 43 * sizeof(WCHAR);
223 
224     pTextBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, uBufferSize);
225     if (!pTextBuffer)
226         return;
227 
228     pLine = pTextBuffer;
229     uOffset = 0;
230 
231     for (i = 0; i < pevlr->DataLength / 8; i++)
232     {
233         pData = (LPBYTE)((LPBYTE)pevlr + pevlr->DataOffset + uOffset);
234 
235         if (bDisplayWords)
236             uLineLength = PrintWordDataLine(pLine, uOffset, (PULONG)pData, 8);
237         else
238             uLineLength = PrintByteDataLine(pLine, uOffset, pData, 8);
239         pLine = pLine + uLineLength;
240 
241         uOffset += 8;
242     }
243 
244     if (pevlr->DataLength % 8 != 0)
245     {
246         pData = (LPBYTE)((LPBYTE)pevlr + pevlr->DataOffset + uOffset);
247 
248         if (bDisplayWords)
249             PrintWordDataLine(pLine, uOffset, (PULONG)pData, pevlr->DataLength % 8);
250         else
251             PrintByteDataLine(pLine, uOffset, pData, pevlr->DataLength % 8);
252     }
253 
254     SetDlgItemTextW(hDlg, IDC_EVENTDATAEDIT, pTextBuffer);
255 
256     HeapFree(GetProcessHeap(), 0, pTextBuffer);
257 }
258 
259 static
260 HFONT
261 CreateMonospaceFont(VOID)
262 {
263     LOGFONTW tmpFont = {0};
264     HFONT hFont;
265     HDC hDC;
266 
267     hDC = GetDC(NULL);
268 
269     tmpFont.lfHeight = -MulDiv(8, GetDeviceCaps(hDC, LOGPIXELSY), 72);
270     tmpFont.lfWeight = FW_NORMAL;
271     wcscpy(tmpFont.lfFaceName, L"Courier New");
272 
273     hFont = CreateFontIndirectW(&tmpFont);
274 
275     ReleaseDC(NULL, hDC);
276 
277     return hFont;
278 }
279 
280 static
281 VOID
282 CopyEventEntry(HWND hWnd)
283 {
284     WCHAR tmpHeader[512];
285     WCHAR szEventType[MAX_PATH];
286     WCHAR szSource[MAX_PATH];
287     WCHAR szCategory[MAX_PATH];
288     WCHAR szEventID[MAX_PATH];
289     WCHAR szDate[MAX_PATH];
290     WCHAR szTime[MAX_PATH];
291     WCHAR szUser[MAX_PATH];
292     WCHAR szComputer[MAX_PATH];
293     WCHAR evtDesc[EVENT_MESSAGE_EVENTTEXT_BUFFER];
294     ULONG size = 0;
295     LPWSTR output;
296     HGLOBAL hMem;
297 
298     /* Try to open the clipboard */
299     if (!OpenClipboard(hWnd))
300         return;
301 
302     /* Get the formatted text needed to place the content into */
303     size += LoadStringW(hInst, IDS_COPY, tmpHeader, ARRAYSIZE(tmpHeader));
304 
305     /* Grab all the information and get it ready for the clipboard */
306     size += GetDlgItemTextW(hWnd, IDC_EVENTTYPESTATIC, szEventType, ARRAYSIZE(szEventType));
307     size += GetDlgItemTextW(hWnd, IDC_EVENTSOURCESTATIC, szSource, ARRAYSIZE(szSource));
308     size += GetDlgItemTextW(hWnd, IDC_EVENTCATEGORYSTATIC, szCategory, ARRAYSIZE(szCategory));
309     size += GetDlgItemTextW(hWnd, IDC_EVENTIDSTATIC, szEventID, ARRAYSIZE(szEventID));
310     size += GetDlgItemTextW(hWnd, IDC_EVENTDATESTATIC, szDate, ARRAYSIZE(szDate));
311     size += GetDlgItemTextW(hWnd, IDC_EVENTTIMESTATIC, szTime, ARRAYSIZE(szTime));
312     size += GetDlgItemTextW(hWnd, IDC_EVENTUSERSTATIC, szUser, ARRAYSIZE(szUser));
313     size += GetDlgItemTextW(hWnd, IDC_EVENTCOMPUTERSTATIC, szComputer, ARRAYSIZE(szComputer));
314     size += GetDlgItemTextW(hWnd, IDC_EVENTTEXTEDIT, evtDesc, ARRAYSIZE(evtDesc));
315 
316     size++; /* Null-termination */
317     size *= sizeof(WCHAR);
318 
319     /*
320      * Consolidate the information into one big piece and
321      * sort out the memory needed to write to the clipboard.
322      */
323     hMem = GlobalAlloc(GMEM_MOVEABLE, size);
324     if (hMem == NULL) goto Quit;
325 
326     output = GlobalLock(hMem);
327     if (output == NULL)
328     {
329         GlobalFree(hMem);
330         goto Quit;
331     }
332 
333     StringCbPrintfW(output, size,
334                     tmpHeader, szEventType, szSource, szCategory, szEventID,
335                     szDate, szTime, szUser, szComputer, evtDesc);
336 
337     GlobalUnlock(hMem);
338 
339     /* We succeeded, empty the clipboard and write the data in it */
340     EmptyClipboard();
341     SetClipboardData(CF_UNICODETEXT, hMem);
342 
343 Quit:
344     /* Close the clipboard once we are done with it */
345     CloseClipboard();
346 }
347 
348 static
349 VOID
350 OnLink(HWND hDlg, ENLINK* penLink)
351 {
352     LPWSTR pLink;
353     TEXTRANGE txtRange;
354 
355     ASSERT(penLink->nmhdr.idFrom == IDC_EVENTTEXTEDIT);
356 
357     /* Only act on left button up events */
358     if (penLink->msg != WM_LBUTTONUP)
359         return;
360 
361     /* If the range is empty, do nothing */
362     if (penLink->chrg.cpMin == penLink->chrg.cpMax)
363         return;
364 
365     /* Allocate memory for the text link */
366     pLink = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
367                       (max(penLink->chrg.cpMin, penLink->chrg.cpMax) -
368                        min(penLink->chrg.cpMin, penLink->chrg.cpMax) + 1) * sizeof(WCHAR));
369     if (!pLink)
370     {
371         /* Not enough memory, bail out */
372         return;
373     }
374 
375     txtRange.chrg = penLink->chrg;
376     txtRange.lpstrText = pLink;
377     SendDlgItemMessageW(hDlg, IDC_EVENTTEXTEDIT, EM_GETTEXTRANGE, 0, (LPARAM)&txtRange);
378 
379     /* Open the link */
380     ShellExecuteW(hDlg, L"open", pLink, NULL, NULL, SW_SHOWNOACTIVATE);
381 
382     /* Free the buffer */
383     HeapFree(GetProcessHeap(), 0, pLink);
384 }
385 
386 static
387 VOID
388 OnScroll(HWND hDlg, PDETAILDATA pData, INT nBar, WORD sbCode)
389 {
390     RECT rect;
391 
392     SCROLLINFO sInfo;
393     INT oldPos, Maximum;
394     PLONG pOriginXY;
395 
396     ASSERT(nBar == SB_HORZ || nBar == SB_VERT);
397 
398     GetClientRect(hDlg, &rect);
399 
400     if (nBar == SB_HORZ)
401     {
402         Maximum = pData->cxMin - (rect.right-rect.left) /* pData->cxOld */;
403         pOriginXY = &pData->scPos.x;
404     }
405     else // if (nBar == SB_VERT)
406     {
407         Maximum = pData->cyMin - (rect.bottom-rect.top) /* pData->cyOld */;
408         pOriginXY = &pData->scPos.y;
409     }
410 
411     /* Set scrollbar sizes */
412     sInfo.cbSize = sizeof(sInfo);
413     sInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE | SIF_TRACKPOS;
414 
415     if (!GetScrollInfo(hDlg, nBar, &sInfo))
416         return;
417 
418     oldPos = sInfo.nPos;
419 
420     switch (sbCode)
421     {
422         case SB_LINEUP:   // SB_LINELEFT:
423             sInfo.nPos--;
424             break;
425 
426         case SB_LINEDOWN: // SB_LINERIGHT:
427             sInfo.nPos++;
428             break;
429 
430         case SB_PAGEUP:   // SB_PAGELEFT:
431             sInfo.nPos -= sInfo.nPage;
432             break;
433 
434         case SB_PAGEDOWN: // SB_PAGERIGHT:
435             sInfo.nPos += sInfo.nPage;
436             break;
437 
438         case SB_THUMBTRACK:
439             sInfo.nPos = sInfo.nTrackPos;
440             break;
441 
442         case SB_THUMBPOSITION:
443             sInfo.nPos = sInfo.nTrackPos;
444             break;
445 
446         case SB_TOP:    // SB_LEFT:
447             sInfo.nPos = sInfo.nMin;
448             break;
449 
450         case SB_BOTTOM: // SB_RIGHT:
451             sInfo.nPos = sInfo.nMax;
452             break;
453 
454         default:
455             break;
456     }
457 
458     sInfo.nPos = min(max(sInfo.nPos, 0), Maximum);
459 
460     if (oldPos != sInfo.nPos)
461     {
462         POINT scOldPos = pData->scPos;
463 
464         /* We now modify pData->scPos */
465         *pOriginXY = sInfo.nPos;
466 
467         ScrollWindowEx(hDlg,
468                        (scOldPos.x - pData->scPos.x),
469                        (scOldPos.y - pData->scPos.y),
470                        NULL,
471                        NULL,
472                        NULL,
473                        NULL,
474                        SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN);
475 
476         sInfo.fMask = SIF_POS;
477         SetScrollInfo(hDlg, nBar, &sInfo, TRUE);
478 
479         // UpdateWindow(hDlg);
480     }
481 }
482 
483 static VOID
484 OnSize(HWND hDlg, PDETAILDATA pData, INT cx, INT cy)
485 {
486     LONG_PTR dwStyle;
487     INT sbVXSize, sbHYSize;
488     SCROLLINFO sInfo;
489     POINT scOldPos;
490     HDWP hdwp;
491     HWND hItemWnd;
492     RECT rect;
493     INT  y = 0;
494 
495     if (!pData)
496         return;
497 
498     dwStyle  = GetWindowLongPtrW(hDlg, GWL_STYLE);
499     sbVXSize = GetSystemMetrics(SM_CXVSCROLL);
500     sbHYSize = GetSystemMetrics(SM_CYHSCROLL);
501 
502     /* Compensate for existing scroll bars (because lParam values do not accommodate scroll bar) */
503     if (dwStyle & WS_HSCROLL) cy += sbHYSize; // Window currently has a horizontal scrollbar
504     if (dwStyle & WS_VSCROLL) cx += sbVXSize; // Window currently has a vertical scrollbar
505 
506     /* Compensate for added scroll bars in window */
507     if (cx < pData->cxMin) cy -= sbHYSize; // Window will have a horizontal scroll bar
508     if (cy < pData->cyMin) cx -= sbVXSize; // Window will have a vertical scroll bar
509 
510     /* Set scrollbar sizes */
511     sInfo.cbSize = sizeof(sInfo);
512 
513     sInfo.fMask = SIF_POS;
514     if (GetScrollInfo(hDlg, SB_VERT, &sInfo))
515         scOldPos.y = sInfo.nPos;
516     else
517         scOldPos.y = pData->scPos.y;
518 
519     sInfo.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
520     sInfo.nMin = 0;
521     if (pData->cyMin > cy)
522     {
523         sInfo.nMax  = pData->cyMin - 1;
524         sInfo.nPage = cy;
525         sInfo.nPos  = pData->scPos.y;
526         SetScrollInfo(hDlg, SB_VERT, &sInfo, TRUE);
527 
528         /* Display the scrollbar if needed */
529         if (!(dwStyle & WS_VSCROLL))
530             ShowScrollBar(hDlg, SB_VERT, TRUE);
531     }
532     else
533     {
534         scOldPos.y = 0;
535 
536         sInfo.nMax  = pData->cyMin - 1;
537         sInfo.nPage = cy;
538         sInfo.nPos  = pData->scPos.y;
539         sInfo.nPos  = scOldPos.y;
540         SetScrollInfo(hDlg, SB_VERT, &sInfo, TRUE);
541 
542         ShowScrollBar(hDlg, SB_VERT, FALSE);
543 
544         rect.left   = cx - sbVXSize;
545         rect.right  = cx;
546         rect.top    = 0;
547         rect.bottom = cy;
548         InvalidateRect(hDlg, &rect, TRUE);
549     }
550 
551     sInfo.fMask = SIF_POS;
552     if (GetScrollInfo(hDlg, SB_HORZ, &sInfo))
553         scOldPos.x = sInfo.nPos;
554     else
555         scOldPos.x = pData->scPos.x;
556 
557     sInfo.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
558     sInfo.nMin = 0;
559     if (pData->cxMin > cx)
560     {
561         sInfo.nMax  = pData->cxMin - 1;
562         sInfo.nPage = cx;
563         sInfo.nPos  = pData->scPos.x;
564         SetScrollInfo(hDlg, SB_HORZ, &sInfo, TRUE);
565 
566         /* Display the scrollbar if needed */
567         if (!(dwStyle & WS_HSCROLL))
568             ShowScrollBar(hDlg, SB_HORZ, TRUE);
569     }
570     else
571     {
572         scOldPos.x = 0;
573 
574         sInfo.nMax  = pData->cxMin - 1;
575         sInfo.nPage = cx;
576         sInfo.nPos  = pData->scPos.x;
577         sInfo.nPos  = scOldPos.x;
578         SetScrollInfo(hDlg, SB_HORZ, &sInfo, TRUE);
579 
580         ShowScrollBar(hDlg, SB_HORZ, FALSE);
581 
582         rect.left   = 0;
583         rect.right  = cx;
584         rect.top    = cy - sbHYSize;
585         rect.bottom = cy;
586         InvalidateRect(hDlg, &rect, TRUE);
587     }
588 
589     if ((scOldPos.x != pData->scPos.x) || (scOldPos.y != pData->scPos.y))
590     {
591         ScrollWindowEx(hDlg,
592                        // (scOldPos.x - pData->scPos.x),
593                        (pData->scPos.x - scOldPos.x),
594                        // (scOldPos.y - pData->scPos.y),
595                        (pData->scPos.y - scOldPos.y),
596                        NULL,
597                        NULL,
598                        NULL,
599                        NULL,
600                        SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN);
601 
602         pData->scPos = scOldPos;
603     }
604 
605     // /* Adjust the start of the visible area if we are attempting to show nonexistent areas */
606     // if ((pData->cxMin - pData->scPos.x) < cx) pData->scPos.x = pData->cxMin - cx;
607     // if ((pData->cyMin - pData->scPos.y) < cy) pData->scPos.y = pData->cyMin - cy;
608     // // InvalidateRect(GuiData->hWindow, NULL, TRUE);
609 
610     /* Forbid resizing the control smaller than its minimal size */
611     if (cx < pData->cxMin) cx = pData->cxMin;
612     if (cy < pData->cyMin) cy = pData->cyMin;
613 
614     if ((cx != pData->cxOld) || (cy != pData->cyOld))
615     {
616         hdwp = BeginDeferWindowPos(8);
617 
618         /* Move the edit boxes */
619 
620         GetWindowRect(hDlg, &rect);
621 
622         hItemWnd = GetDlgItem(hDlg, IDC_EVENTTEXTEDIT);
623         GetWindowRect(hItemWnd, &rect);
624         MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT));
625         // OffsetRect(&rect, 0, y);
626         // y += (cy - pData->cyOld) / 2 ; // + (cy - pData->cyOld) % 2;
627         /** y += (cy - pData->cyOld) / 2 ; // + (cy - pData->cyOld) % 2; **/
628         if (cy >= pData->cyOld)
629             y += (cy - pData->cyOld) / 2 + (cy - pData->cyOld) % 2;
630         else
631             y -= (pData->cyOld - cy) / 2 + (pData->cyOld - cy) % 2;
632 
633         if (hdwp)
634             hdwp = DeferWindowPos(hdwp,
635                                   hItemWnd,
636                                   0,
637                                   rect.left, rect.top,
638                                   (rect.right - rect.left) + (cx - pData->cxOld),
639                                   (rect.bottom - rect.top) + y,
640                                   /** SWP_NOMOVE | **/ SWP_NOZORDER | SWP_NOACTIVATE);
641 
642         hItemWnd = GetDlgItem(hDlg, IDC_DETAILS_STATIC);
643         GetWindowRect(hItemWnd, &rect);
644         MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT));
645         // OffsetRect(&rect, 0, y);
646 
647         if (hdwp)
648             hdwp = DeferWindowPos(hdwp,
649                                   hItemWnd,
650                                   0,
651                                   rect.left, rect.top + y,
652                                   0, 0,
653                                   SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
654 
655         hItemWnd = GetDlgItem(hDlg, IDC_BYTESRADIO);
656         GetWindowRect(hItemWnd, &rect);
657         MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT));
658         // OffsetRect(&rect, 0, y);
659 
660         if (hdwp)
661             hdwp = DeferWindowPos(hdwp,
662                                   hItemWnd,
663                                   0,
664                                   rect.left, rect.top + y,
665                                   0, 0,
666                                   SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
667 
668         hItemWnd = GetDlgItem(hDlg, IDC_WORDRADIO);
669         GetWindowRect(hItemWnd, &rect);
670         MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT));
671         // OffsetRect(&rect, 0, y);
672 
673         if (hdwp)
674             hdwp = DeferWindowPos(hdwp,
675                                   hItemWnd,
676                                   0,
677                                   rect.left, rect.top + y,
678                                   0, 0,
679                                   SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
680 
681         hItemWnd = GetDlgItem(hDlg, IDC_EVENTDATAEDIT);
682         GetWindowRect(hItemWnd, &rect);
683         MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT));
684         // OffsetRect(&rect, 0, y);
685         // // y -= (cy - pData->cyOld) % 2;
686 
687         if (hdwp)
688             hdwp = DeferWindowPos(hdwp,
689                                   hItemWnd,
690                                   0,
691                                   rect.left, rect.top + y,
692                                   (rect.right - rect.left) + (cx - pData->cxOld),
693                                   (rect.bottom - rect.top) + y,
694                                   SWP_NOZORDER | SWP_NOACTIVATE);
695 
696         /* Move the buttons */
697 
698         hItemWnd = GetDlgItem(hDlg, IDC_PREVIOUS);
699         GetWindowRect(hItemWnd, &rect);
700         MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT));
701 
702         if (hdwp)
703             hdwp = DeferWindowPos(hdwp,
704                                   hItemWnd,
705                                   0,
706                                   rect.left + (cx - pData->cxOld),
707                                   rect.top,
708                                   0, 0,
709                                   SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
710 
711         hItemWnd = GetDlgItem(hDlg, IDC_NEXT);
712         GetWindowRect(hItemWnd, &rect);
713         MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT));
714 
715         if (hdwp)
716             hdwp = DeferWindowPos(hdwp,
717                                   hItemWnd,
718                                   0,
719                                   rect.left + (cx - pData->cxOld),
720                                   rect.top,
721                                   0, 0,
722                                   SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
723 
724         hItemWnd = GetDlgItem(hDlg, IDC_COPY);
725         GetWindowRect(hItemWnd, &rect);
726         MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT));
727 
728         if (hdwp)
729             hdwp = DeferWindowPos(hdwp,
730                                   hItemWnd,
731                                   0,
732                                   rect.left + (cx - pData->cxOld),
733                                   rect.top,
734                                   0, 0,
735                                   SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
736 
737         if (hdwp)
738             EndDeferWindowPos(hdwp);
739 
740         pData->cxOld = cx;
741         pData->cyOld = cy;
742     }
743 }
744 
745 static
746 VOID
747 InitDetailsDlgCtrl(HWND hDlg, PDETAILDATA pData)
748 {
749     DWORD dwMask;
750 
751     HANDLE nextIcon = LoadImageW(hInst, MAKEINTRESOURCEW(IDI_NEXT), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
752     HANDLE prevIcon = LoadImageW(hInst, MAKEINTRESOURCEW(IDI_PREV), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
753     HANDLE copyIcon = LoadImageW(hInst, MAKEINTRESOURCEW(IDI_COPY), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
754 
755     SendDlgItemMessageW(hDlg, IDC_NEXT, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)nextIcon);
756     SendDlgItemMessageW(hDlg, IDC_PREVIOUS, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)prevIcon);
757     SendDlgItemMessageW(hDlg, IDC_COPY, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)copyIcon);
758 
759     /* Set the default read-only RichEdit color */
760     SendDlgItemMessageW(hDlg, IDC_EVENTTEXTEDIT, EM_SETBKGNDCOLOR, 0, GetSysColor(COLOR_3DFACE));
761 
762     /* Enable RichEdit coloured and underlined links */
763     dwMask = SendDlgItemMessageW(hDlg, IDC_EVENTTEXTEDIT, EM_GETEVENTMASK, 0, 0);
764     SendDlgItemMessageW(hDlg, IDC_EVENTTEXTEDIT, EM_SETEVENTMASK, 0, dwMask | ENM_LINK | ENM_MOUSEEVENTS);
765 
766     /*
767      * Activate automatic URL recognition by the RichEdit control. For more information, see:
768      * https://blogs.msdn.microsoft.com/murrays/2009/08/31/automatic-richedit-hyperlinks/
769      * https://blogs.msdn.microsoft.com/murrays/2009/09/24/richedit-friendly-name-hyperlinks/
770      * https://msdn.microsoft.com/en-us/library/windows/desktop/bb787991(v=vs.85).aspx
771      */
772     SendDlgItemMessageW(hDlg, IDC_EVENTTEXTEDIT, EM_AUTOURLDETECT, AURL_ENABLEURL /* | AURL_ENABLEEAURLS */, 0);
773 
774     /* Note that the RichEdit control never gets themed under WinXP+; one would have to write code to simulate Edit-control theming */
775 
776     SendDlgItemMessageW(hDlg, pData->bDisplayWords ? IDC_WORDRADIO : IDC_BYTESRADIO, BM_SETCHECK, BST_CHECKED, 0);
777     SendDlgItemMessageW(hDlg, IDC_EVENTDATAEDIT, WM_SETFONT, (WPARAM)pData->hMonospaceFont, (LPARAM)TRUE);
778 }
779 
780 /* Message handler for Event Details control */
781 static
782 INT_PTR CALLBACK
783 EventDetailsCtrl(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
784 {
785     PDETAILDATA pData;
786 
787     pData = (PDETAILDATA)GetWindowLongPtrW(hDlg, DWLP_USER);
788 
789     switch (uMsg)
790     {
791         case WM_INITDIALOG:
792         {
793             RECT rect;
794 
795             pData = (PDETAILDATA)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pData));
796             if (!pData)
797             {
798                 EndDialog(hDlg, 0);
799                 return (INT_PTR)TRUE;
800             }
801             SetWindowLongPtrW(hDlg, DWLP_USER, (LONG_PTR)pData);
802 
803             pData->EventLogFilter = (PEVENTLOGFILTER)lParam;
804             pData->bDisplayWords  = FALSE;
805             pData->hMonospaceFont = CreateMonospaceFont();
806 
807             GetClientRect(hDlg, &rect);
808             pData->cxOld = pData->cxMin = rect.right - rect.left;
809             pData->cyOld = pData->cyMin = rect.bottom - rect.top;
810             pData->scPos.x = pData->scPos.y = 0;
811 
812             InitDetailsDlgCtrl(hDlg, pData);
813 
814 #if 0
815             /* Show event info on dialog box */
816             DisplayEvent(hDlg, pData->EventLogFilter);
817             DisplayEventData(hDlg, pData->bDisplayWords);
818 #endif
819 
820             // OnSize(hDlg, pData, pData->cxOld, pData->cyOld);
821             return (INT_PTR)TRUE;
822         }
823 
824         case WM_DESTROY:
825             if (pData)
826             {
827                 if (pData->hMonospaceFont)
828                     DeleteObject(pData->hMonospaceFont);
829                 HeapFree(GetProcessHeap(), 0, pData);
830             }
831             return (INT_PTR)TRUE;
832 
833         case EVT_SETFILTER:
834             pData->EventLogFilter = (PEVENTLOGFILTER)lParam;
835             return (INT_PTR)TRUE;
836 
837         case EVT_DISPLAY:
838             if (pData->EventLogFilter)
839             {
840                 /* Show event info on dialog box */
841                 DisplayEvent(hDlg, pData->EventLogFilter);
842                 DisplayEventData(hDlg, pData->bDisplayWords);
843             }
844             return (INT_PTR)TRUE;
845 
846         case WM_COMMAND:
847             switch (LOWORD(wParam))
848             {
849                 case IDC_PREVIOUS:
850                 {
851                     SendMessageW(hwndListView, WM_KEYDOWN, VK_UP, 0);
852 
853                     /* Show event info on dialog box */
854                     if (pData->EventLogFilter)
855                     {
856                         DisplayEvent(hDlg, pData->EventLogFilter);
857                         DisplayEventData(hDlg, pData->bDisplayWords);
858                     }
859                     return (INT_PTR)TRUE;
860                 }
861 
862                 case IDC_NEXT:
863                 {
864                     SendMessageW(hwndListView, WM_KEYDOWN, VK_DOWN, 0);
865 
866                     /* Show event info on dialog box */
867                     if (pData->EventLogFilter)
868                     {
869                         DisplayEvent(hDlg, pData->EventLogFilter);
870                         DisplayEventData(hDlg, pData->bDisplayWords);
871                     }
872                     return (INT_PTR)TRUE;
873                 }
874 
875                 case IDC_COPY:
876                     if (pData->EventLogFilter)
877                         CopyEventEntry(hDlg);
878                     return (INT_PTR)TRUE;
879 
880                 case IDC_BYTESRADIO:
881                     if (pData->EventLogFilter)
882                     {
883                         pData->bDisplayWords = FALSE;
884                         DisplayEventData(hDlg, pData->bDisplayWords);
885                     }
886                     return (INT_PTR)TRUE;
887 
888                 case IDC_WORDRADIO:
889                     if (pData->EventLogFilter)
890                     {
891                         pData->bDisplayWords = TRUE;
892                         DisplayEventData(hDlg, pData->bDisplayWords);
893                     }
894                     return (INT_PTR)TRUE;
895 
896                 default:
897                     break;
898             }
899             break;
900 
901         case WM_NOTIFY:
902         {
903             LPNMHDR hdr = (LPNMHDR)lParam;
904 
905             if (hdr->idFrom == IDC_EVENTTEXTEDIT)
906             {
907                 switch (hdr->code)
908                 {
909                     case EN_LINK:
910                         OnLink(hDlg, (ENLINK*)lParam);
911                         break;
912                 }
913             }
914             break;
915         }
916 
917         case WM_HSCROLL:
918             OnScroll(hDlg, pData, SB_HORZ, LOWORD(wParam));
919             SetWindowLongPtrW(hDlg, DWLP_MSGRESULT, 0);
920             return (INT_PTR)TRUE;
921 
922         case WM_VSCROLL:
923             OnScroll(hDlg, pData, SB_VERT, LOWORD(wParam));
924             SetWindowLongPtrW(hDlg, DWLP_MSGRESULT, 0);
925             return (INT_PTR)TRUE;
926 
927         case WM_SIZE:
928             OnSize(hDlg, pData, LOWORD(lParam), HIWORD(lParam));
929             SetWindowLongPtrW(hDlg, DWLP_MSGRESULT, 0);
930             return (INT_PTR)TRUE;
931     }
932 
933     return (INT_PTR)FALSE;
934 }
935 
936 HWND
937 CreateEventDetailsCtrl(HINSTANCE hInstance,
938                        HWND hParentWnd,
939                        LPARAM lParam)
940 {
941     return CreateDialogParamW(hInstance,
942                               MAKEINTRESOURCEW(IDD_EVENTDETAILS_CTRL),
943                               hParentWnd, EventDetailsCtrl, lParam);
944 }
945